# Cognitive Flexibility Test - Frontend Implementation Plan

## Document Overview

This document provides a comprehensive plan for implementing the Cognitive Flexibility Test (Probabilistic Reversal Learning Task) frontend application. It addresses all critical issues from the prototype and ensures strict adherence to the test specifications outlined in the research documentation.

---

## Table of Contents

1. [Project Architecture](#1-project-architecture)
2. [Test Specifications & Requirements](#2-test-specifications--requirements)
3. [State Management Strategy](#3-state-management-strategy)
4. [Screen Flow Implementation](#4-screen-flow-implementation)
5. [Image Asset Management](#5-image-asset-management)
6. [Core Logic Implementation](#6-core-logic-implementation)
7. [Data Collection & Recording](#7-data-collection--recording)
8. [Timing & Performance](#8-timing--performance)
9. [Error Handling & Edge Cases](#9-error-handling--edge-cases)
10. [Testing Strategy](#10-testing-strategy)
11. [Future API Integration](#11-future-api-integration)

---

## 1. Project Architecture

### 1.1 Technology Stack

**Core Framework:**
- React 18+ with TypeScript
- React Hooks for state management
- CSS-in-JS or Tailwind CSS for styling

**Key Libraries:**
- None required for core functionality (keep dependencies minimal)
- Optional: date-fns for timestamp formatting
- Optional: react-confetti for celebration effects

### 1.2 Folder Structure

```
src/
├── components/
│   ├── screens/
│   │   ├── IntroScreen.tsx
│   │   ├── PracticeScreen.tsx
│   │   ├── MainTestScreen.tsx
│   │   └── ResultsScreen.tsx
│   ├── shared/
│   │   ├── StimulusDisplay.tsx
│   │   ├── FeedbackDisplay.tsx
│   │   ├── FixationCross.tsx
│   │   ├── Timer.tsx
│   │   └── ProgressBar.tsx
│   └── CognitiveFlexibilityTest.tsx (main orchestrator)
├── hooks/
│   ├── useTestState.ts
│   ├── useTimer.ts
│   ├── useDataRecorder.ts
│   └── useReversalLogic.ts
├── types/
│   ├── test.types.ts
│   └── data.types.ts
├── constants/
│   ├── testConfig.ts
│   └── stimuliConfig.ts
├── utils/
│   ├── dataExport.ts
│   ├── calculations.ts
│   └── validation.ts
└── public/
    └── stimuli/
        ├── adolescent/
        │   ├── practice/
        │   │   ├── purple-pen.png
        │   │   └── pink-pen.png
        │   ├── blocks-1-2/
        │   │   ├── golden-treasure-box.png
        │   │   └── silver-treasure-box.png
        │   ├── blocks-3-4/
        │   │   ├── purple-pen.png
        │   │   └── pink-pen.png
        │   └── blocks-5-6/
        │       ├── yellow-key.png
        │       └── green-key.png
        └── adult/
            ├── practice/
            │   ├── star-oval-diamond.png
            │   └── diamond-rectangle.png
            ├── blocks-1-2/
            │   ├── blue-cube.png
            │   └── yellow-square.png
            ├── blocks-3-4/
            │   ├── star-purple-oval.png
            │   └── heart-diamond-rectangle.png
            └── blocks-5-6/
                ├── horizontal-lines.png
                └── vertical-lines.png
```

---

## 2. Test Specifications & Requirements

### 2.1 Test Structure (CORRECTED)

**Practice Phase:**
- 12 rounds
- Uses practice stimuli
- 25% probabilistic feedback
- NO reversals allowed
- Same timing as main test

**Main Test Phase:**
- 6 blocks × 12 rounds = **72 total rounds**
- Different stimulus pair for each block pair
- Reversals triggered after 3 consecutive correct responses
- Block instruction screen before each block

### 2.2 Age Group Configurations

#### Adolescents (14-18 years)

**Stimuli:**
| Block | Left Stimulus | Right Stimulus | Initial Correct |
|-------|---------------|----------------|-----------------|
| Practice | Purple Pen | Pink Pen | Purple Pen |
| Block 1-2 | Golden Treasure Box | Silver Treasure Box | Golden Box |
| Block 3-4 | Purple Pen | Pink Pen | Purple Pen |
| Block 5-6 | Yellow Key | Green Key | Yellow Key |

**Feedback:**
- Correct: +110 gold coins with coin icon
- Incorrect: -40 gold coins with broken coin icon
- Starting score: 3,000 coins
- Score always visible

#### Adults (18-22 years)

**Stimuli:**
| Block | Left Stimulus | Right Stimulus | Initial Correct |
|-------|---------------|----------------|-----------------|
| Practice | Star+Oval+Diamond | Diamond+Rectangle | Star+Oval+Diamond |
| Block 1-2 | Blue Cube | Yellow Square | Blue Cube |
| Block 3-4 | Yellow Star+Purple Oval | Red Heart+Blue Diamond+Green Rectangle | Star+Oval |
| Block 5-6 | Horizontal Lines | Vertical Lines | Horizontal Lines |

**Feedback:**
- Correct: Green smiley face 😊
- Incorrect: Red sad face 😢
- No scoring system (only emotional feedback)

### 2.3 Timing Specifications

```typescript
const TIMING_CONFIG = {
  RESPONSE_WINDOW: 4000,    // 4 seconds exact
  FEEDBACK_DURATION: 1000,  // 1 second
  FIXATION_DURATION: 300,   // 0.3 seconds
  TOTAL_TRIAL: 5300         // 5.3 seconds per trial
};
```

### 2.4 Feedback Distribution

**75% Contingent (Normal):**
- Correct choice → Reward
- Incorrect choice → Punishment
- 9 out of 12 trials per block

**25% Non-Contingent (Misleading):**
- Correct choice → Punishment (misleading)
- Incorrect choice → Reward (misleading)
- 3 out of 12 trials per block

**Random Distribution Patterns:**
- All 3 misleading punishments, OR
- 2 misleading punishments + 1 misleading reward, OR
- 2 misleading rewards + 1 misleading punishment

---

## 3. State Management Strategy

### 3.1 Core State Structure

```typescript
interface TestState {
  // Navigation
  currentScreen: 'intro' | 'practice' | 'practiceComplete' | 'main' | 'blockInstruction' | 'results';
  ageGroup: 'adolescent' | 'adult';
  
  // Progress tracking
  currentBlock: number;        // 0-5 (6 blocks total)
  currentRound: number;        // 0-11 (12 rounds per block)
  
  // Trial state
  leftStimulusPath: string;
  rightStimulusPath: string;
  currentCorrectSide: 'left' | 'right';
  selectedSide: 'left' | 'right' | null;
  
  // Timing
  trialStartTime: number;
  responseTime: number | null;
  timeRemaining: number;
  
  // Feedback
  showFeedback: boolean;
  feedbackType: 'correct' | 'incorrect';
  isProbabilisticFeedback: boolean;
  showFixation: boolean;
  showTimeout: boolean;
  
  // Scoring (adolescents only)
  score: number;
  
  // Reversal tracking
  consecutiveCorrectCount: number;
  reversalCount: number;
  lastReversalTrial: number | null;
  errorsSinceReversal: number;
  
  // Data collection
  trialData: TrialRecord[];
  
  // Error counts
  totalReversalErrors: number;
  totalPerseverativeErrors: number;
  totalFinalReversalErrors: number;
  
  // Shift tracking
  lastChosenStimulus: string | null;
  lastOutcome: 'win' | 'loss' | null;
  winShifts: number;
  loseShifts: number;
  totalWins: number;
  totalLosses: number;
}
```

### 3.2 Trial Data Recording Structure

```typescript
interface TrialRecord {
  // Identifiers
  trialNumber: number;              // 1-84 (12 practice + 72 main)
  blockNumber: number | 'Practice'; // 'Practice' or 1-6
  roundInBlock: number;             // 1-12
  
  // Stimulus information
  stimulusSet: string;              // e.g., "Golden Box vs Silver Box"
  leftStimulus: string;             // Image filename
  rightStimulus: string;            // Image filename
  
  // Task rule
  taskRule: string;                 // e.g., "Golden Box is rewarded"
  currentCorrectStimulus: string;   // e.g., "Golden Box"
  switchIndicator: boolean;         // True if reversal occurred this trial
  
  // Response
  participantChoice: string | 'timeout';  // Chosen stimulus name or 'timeout'
  chosenSide: 'left' | 'right' | null;
  correctResponse: string;          // Expected correct stimulus
  responseAccuracy: 0 | 1;          // 0 = incorrect, 1 = correct
  responseTime: number;             // Milliseconds
  
  // Feedback
  isProbabilistic: boolean;         // Was this misleading feedback?
  feedbackGiven: string;            // e.g., "+110 coins" or "Green Smiley"
  feedbackType: 'reward' | 'punishment';
  
  // Error classification
  errorType: 'none' | 'reversal' | 'perseverative' | 'final_reversal' | 'random';
  
  // Reversal tracking
  reversalTriggered: boolean;       // Did this trial trigger a reversal?
  consecutiveCorrectBeforeTrial: number;
  
  // Score (adolescents only)
  scoreChange: number;              // +110, -40, or 0
  totalScore: number;
  
  // Timestamp
  timestamp: number;
}
```

### 3.3 State Separation Strategy

**Use Custom Hooks for Modularity:**

```typescript
// Main orchestrator
const useTestState = () => {
  // Core navigation and progress state
};

// Separate concerns
const useTimer = (duration: number, onComplete: () => void);
const useReversalLogic = (consecutiveCorrect: number, isPractice: boolean);
const useDataRecorder = (trialData: TrialRecord[]);
const useShiftTracking = (lastChoice: string, lastOutcome: string);
```

---

## 4. Screen Flow Implementation

### 4.1 Screen: Intro (Age Selection + Instructions)

**Purpose:** Welcome screen, age group selection, and initial instructions.

**UI Components:**
- Welcome message with brain icon
- Age group selector (buttons or dropdown)
  - "Adolescent (14-18 years)"
  - "Adult (18-22 years)"
- Instructions tailored to selected age group
- Warning about rule changes
- "Let's Practice!" button

**Key Logic:**
```typescript
const IntroScreen = ({ onStart, onAgeGroupSelect }) => {
  const [selectedAge, setSelectedAge] = useState<'adolescent' | 'adult' | null>(null);
  
  const instructions = {
    adolescent: "Welcome to the Game! Two pictures will appear—tap the one you think hides the gold coins...",
    adult: "Welcome! You will see two shapes or designs side by side on the screen..."
  };
  
  return (
    // Display age selection first
    // Then show instructions based on selection
    // Enable "Let's Practice!" only when age selected
  );
};
```

**Critical Implementation Notes:**
- Age group MUST be selected before proceeding
- Instructions must match PDF text exactly
- Store age group in state for entire test session
- Different stimuli and feedback will be loaded based on selection

---

### 4.2 Screen: Practice (12 Rounds)

**Purpose:** Familiarize user with mechanics, NO reversals.

**Header Display:**
- "Practice Round - Round X/12"
- Score display (adolescents only)
- Progress bar

**Core Components:**
- Timer countdown (4 seconds)
- Two stimulus images (left/right randomized)
- Feedback display
- Fixation cross (+)

**Critical Rules:**
1. **NO REVERSAL LOGIC** - Even if 3+ consecutive correct
2. Probabilistic feedback still active (25%)
3. Same timing as main test
4. Starting correct stimulus defined in config
5. Timeout handling with score deduction

**Practice Complete Transition:**
```typescript
// After 12 rounds complete
const handlePracticeComplete = () => {
  // Show intermediate screen
  setCurrentScreen('practiceComplete');
  // Display: "Great job! Now ready for real challenge"
  // Reset relevant counters but keep age group
};
```

---

### 4.3 Screen: Block Instructions (Between Main Test Blocks)

**Purpose:** Brief instruction before each of 6 blocks.

**Display Requirements:**
- Block number (1-6)
- New stimulus set preview (optional)
- Brief instruction text
- "Continue" or "Let's Go!" button

**Example Instructions (FROM PDF):**
- Block 1: "Welcome to the Game! [full instructions]"
- Block 2 (Adolescent): "Purple Pen will provide reward for this block."
- Block 3 (Adolescent): "Green Key will provide reward for this block."
- Block 2 (Adult): "[Stimulus] will provide reward for this block."
- (Pattern continues as specified in PDF Section 5 Main Task Flow Table)

**Implementation:**
```typescript
const BlockInstructionScreen = ({ blockNumber, stimulusName, onContinue }) => {
  const instructions = {
    1: "Welcome to the Game! [full text]",
    2: `${stimulusName} will provide the reward from this block.`,
    // ... for all 6 blocks
  };
  
  return (
    // Show block instruction
    // "Let's Go!" button calls onContinue()
  );
};
```

**Timing:** No time limit, user proceeds when ready.

---

### 4.4 Screen: Main Test (72 Rounds across 6 Blocks)

**Purpose:** Core cognitive flexibility assessment with reversals.

**Header Display:**
- "Block X - Round Y/12"
- Score (adolescents) or blank (adults)
- Progress bar (per block or overall)

**Trial Flow (5.3 seconds total):**

1. **Stimulus Display + Response (0-4 seconds)**
   - Show two images
   - Timer counts down
   - User taps/clicks one image
   - Selected image gets blue outline immediately
   - OR timeout shows "Time is up!"

2. **Feedback Display (1 second)**
   - Stimuli remain visible
   - Feedback overlays (coins/smiley)
   - Score updates (adolescents)

3. **Fixation Cross (0.3 seconds)**
   - Clear screen
   - Show "+" symbol
   - Prepare for next trial

**Block Transitions:**
- After round 12 of each block → Block Instruction Screen
- Continue until 6 blocks complete

**Critical Implementation Points:**

```typescript
const MainTestScreen = () => {
  // Check if reversal should be disabled
  const isPractice = currentScreen === 'practice';
  const canTriggerReversal = !isPractice;
  
  // Handle choice
  const handleChoice = (side: 'left' | 'right') => {
    const responseTime = Date.now() - trialStartTime;
    const chosenStimulus = side === 'left' ? leftStimulus : rightStimulus;
    const correctStimulus = currentCorrectSide === 'left' ? leftStimulus : rightStimulus;
    
    // Determine TRUE correctness (internal)
    const isActuallyCorrect = chosenStimulus === correctStimulus;
    
    // Determine DISPLAYED feedback (may be misleading)
    const isProbabilistic = shouldApplyProbabilisticFeedback();
    const displayAsCorrect = isProbabilistic ? !isActuallyCorrect : isActuallyCorrect;
    
    // Track ACTUAL correctness for reversal logic
    if (isActuallyCorrect && canTriggerReversal) {
      incrementConsecutiveCorrect();
      if (consecutiveCorrect >= 3) {
        triggerReversal();
      }
    } else {
      resetConsecutiveCorrect();
    }
    
    // Track errors BASED ON ACTUAL correctness
    if (!isActuallyCorrect) {
      classifyError();
    }
    
    // Update score BASED ON DISPLAYED feedback
    updateScore(displayAsCorrect);
    
    // Track shifts BASED ON CURRENT outcome
    trackShifts(chosenStimulus, displayAsCorrect);
    
    // Record trial data
    recordTrial({...});
    
    // Show feedback
    showFeedback(displayAsCorrect);
  };
};
```

---

### 4.5 Screen: Results

**Purpose:** Display comprehensive performance analysis.

**Display Sections:**

1. **Summary Metrics (Grid Layout)**
   - Accuracy percentage
   - Average response time
   - Total correct responses
   - Reversals triggered

2. **Error Analysis**
   - Reversal Errors: X (with explanation)
   - Perseverative Errors: X (with explanation)
   - Final Reversal Errors: X (with explanation)

3. **Strategy Flexibility**
   - Win-Shift Rate: X% (Lower is better)
   - Lose-Shift Rate: X% (Higher is better)

4. **Performance Summary**
   - Total rounds: 72
   - Rounds answered: X
   - Rounds missed: X
   - Final score: X coins (adolescents only)

5. **Interpretation Guide**
   - What high accuracy means
   - What few reversal errors indicate
   - Cognitive flexibility indicators

**Actions:**
- "Restart Test" button
- "Export Data" button (CSV/JSON)
- "View Detailed Report" (optional)

**Calculations:**

```typescript
const calculateResults = (trialData: TrialRecord[]) => {
  const mainTestData = trialData.filter(t => t.blockNumber !== 'Practice');
  
  const correctCount = mainTestData.filter(t => t.responseAccuracy === 1 && t.participantChoice !== 'timeout').length;
  const totalResponded = mainTestData.filter(t => t.participantChoice !== 'timeout').length;
  const accuracy = (correctCount / totalResponded) * 100;
  
  const avgRT = mainTestData
    .filter(t => t.participantChoice !== 'timeout')
    .reduce((sum, t) => sum + t.responseTime, 0) / totalResponded;
  
  const winShiftRate = totalWins > 0 ? (winShifts / totalWins) * 100 : 0;
  const loseShiftRate = totalLosses > 0 ? (loseShifts / totalLosses) * 100 : 0;
  
  return {
    accuracy: accuracy.toFixed(1),
    avgRT: avgRT.toFixed(0),
    correctCount,
    totalResponded,
    totalUnanswered: mainTestData.filter(t => t.participantChoice === 'timeout').length,
    reversalCount,
    reversalErrors,
    perseverativeErrors,
    finalReversalErrors,
    winShiftRate: winShiftRate.toFixed(1),
    loseShiftRate: loseShiftRate.toFixed(1)
  };
};
```

---

## 5. Image Asset Management

### 5.1 Image Requirements

**Format:** PNG with transparent background (recommended)
**Size:** 400x400px (adjust based on display needs)
**Quality:** High resolution for clarity
**Naming Convention:** kebab-case (e.g., `golden-treasure-box.png`)

### 5.2 Local Storage Structure

```
public/stimuli/
├── adolescent/
│   ├── practice/
│   │   ├── purple-pen.png
│   │   └── pink-pen.png
│   ├── golden-treasure-box.png
│   ├── silver-treasure-box.png
│   ├── purple-pen.png (duplicate, different context)
│   ├── pink-pen.png (duplicate, different context)
│   ├── yellow-key.png
│   └── green-key.png
└── adult/
    ├── practice/
    │   ├── star-oval-diamond.png
    │   └── diamond-rectangle.png
    ├── blue-cube.png
    ├── yellow-square.png
    ├── star-purple-oval.png
    ├── heart-diamond-rectangle.png
    ├── horizontal-lines.png
    └── vertical-lines.png
```

### 5.3 Configuration File

```typescript
// constants/stimuliConfig.ts
export const STIMULI_CONFIG = {
  adolescent: {
    practice: {
      left: '/stimuli/adolescent/practice/purple-pen.png',
      right: '/stimuli/adolescent/practice/pink-pen.png',
      correct: 'left',
      names: { left: 'Purple Pen', right: 'Pink Pen' }
    },
    blocks: [
      { // Block 1-2
        left: '/stimuli/adolescent/golden-treasure-box.png',
        right: '/stimuli/adolescent/silver-treasure-box.png',
        correct: 'left',
        names: { left: 'Golden Treasure Box', right: 'Silver Treasure Box' }
      },
      { // Block 3-4
        left: '/stimuli/adolescent/purple-pen.png',
        right: '/stimuli/adolescent/pink-pen.png',
        correct: 'left',
        names: { left: 'Purple Pen', right: 'Pink Pen' }
      },
      { // Block 5-6
        left: '/stimuli/adolescent/yellow-key.png',
        right: '/stimuli/adolescent/green-key.png',
        correct: 'left',
        names: { left: 'Yellow Key', right: 'Green Key' }
      }
    ]
  },
  adult: {
    // Similar structure for adult stimuli
  }
};
```

### 5.4 Image Loading Strategy

```typescript
const StimulusImage = ({ src, alt, isSelected, onClick, disabled }) => {
  const [isLoaded, setIsLoaded] = useState(false);
  const [hasError, setHasError] = useState(false);
  
  return (
    <div className={`stimulus-container ${isSelected ? 'selected' : ''}`}>
      {!isLoaded && <LoadingSpinner />}
      <img
        src={src}
        alt={alt}
        onLoad={() => setIsLoaded(true)}
        onError={() => setHasError(true)}
        onClick={!disabled ? onClick : undefined}
        style={{ display: isLoaded ? 'block' : 'none' }}
      />
      {hasError && <div>Error loading image</div>}
    </div>
  );
};
```

**Preloading Strategy:**
```typescript
// Preload all images for current block and next block
useEffect(() => {
  const imagesToPreload = [
    ...getCurrentBlockImages(),
    ...getNextBlockImages()
  ];
  
  imagesToPreload.forEach(src => {
    const img = new Image();
    img.src = src;
  });
}, [currentBlock]);
```

### 5.5 Future API Integration Preparation

```typescript
// utils/imageLoader.ts
export const getImageSource = async (
  ageGroup: string,
  blockType: string,
  stimulusName: string
): Promise<string> => {
  // Current: Return local path
  if (USE_LOCAL_IMAGES) {
    return `/stimuli/${ageGroup}/${blockType}/${stimulusName}.png`;
  }
  
  // Future: Fetch from API
  try {
    const response = await fetch(`/api/stimuli/${ageGroup}/${blockType}/${stimulusName}`);
    const blob = await response.blob();
    return URL.createObjectURL(blob);
  } catch (error) {
    console.error('Failed to fetch image:', error);
    return '/stimuli/fallback.png';
  }
};
```

---

## 6. Core Logic Implementation

### 6.1 Reversal Logic (CRITICAL)

**Trigger Condition:** After 3 consecutive ACTUAL correct responses

**Key Points:**
1. Count based on ACTUAL correctness, NOT displayed feedback
2. Misleading punishment STILL counts as correct for reversal
3. Only active during main test (disabled in practice)
4. Resets to 0 after trigger or after any incorrect choice

```typescript
const handleReversalLogic = (isActuallyCorrect: boolean, isPractice: boolean) => {
  if (isPractice) {
    // NO reversals during practice
    return { shouldReverse: false };
  }
  
  if (isActuallyCorrect) {
    const newCount = consecutiveCorrectCount + 1;
    setConsecutiveCorrectCount(newCount);
    
    if (newCount >= 3) {
      // TRIGGER REVERSAL
      const previousCorrectSide = currentCorrectSide;
      const newCorrectSide = previousCorrectSide === 'left' ? 'right' : 'left';
      
      setCurrentCorrectSide(newCorrectSide);
      setConsecutiveCorrectCount(0);
      setReversalCount(prev => prev + 1);
      setLastReversalTrial(currentTrialNumber);
      setErrorsSinceReversal(0);
      
      return { 
        shouldReverse: true, 
        previousSide: previousCorrectSide,
        newSide: newCorrectSide
      };
    }
  } else {
    // Incorrect choice resets counter
    setConsecutiveCorrectCount(0);
  }
  
  return { shouldReverse: false };
};
```

### 6.2 Probabilistic Feedback Logic

**Distribution:** 25% of trials (3 out of 12 per block)

**Implementation Strategy:**

```typescript
// Generate probabilistic pattern at block start
const generateProbabilisticPattern = (totalRounds: number = 12) => {
  const probabilisticCount = 3;
  const rounds = Array.from({ length: totalRounds }, (_, i) => i);
  
  // Shuffle and select 3 random rounds
  const shuffled = rounds.sort(() => Math.random() - 0.5);
  const probabilisticRounds = shuffled.slice(0, probabilisticCount);
  
  // Randomly determine type for each
  const pattern = probabilisticRounds.map(round => ({
    round,
    type: Math.random() > 0.5 ? 'misleading_punishment' : 'misleading_reward'
  }));
  
  return pattern;
};

// Check if current round should have probabilistic feedback
const shouldApplyProbabilisticFeedback = (currentRound: number, pattern: any[]) => {
  return pattern.find(p => p.round === currentRound);
};

// Apply probabilistic feedback
const determineFeedback = (isActuallyCorrect: boolean, probabilisticInfo: any) => {
  if (!probabilisticInfo) {
    // Normal contingent feedback
    return isActuallyCorrect ? 'reward' : 'punishment';
  }
  
  // Non-contingent (misleading) feedback
  if (probabilisticInfo.type === 'misleading_punishment') {
    return isActuallyCorrect ? 'punishment' : 'reward';
  } else {
    return isActuallyCorrect ? 'reward' : 'punishment';
  }
};
```

**Critical Rules:**
- Generate pattern ONCE per block (not per trial)
- Can be any combination: 3P, 2P+1R, 2R+1P (P=punishment, R=reward)
- Participant should NOT be able to predict pattern

### 6.3 Error Classification Logic

**Three Error Types to Track:**

```typescript
const classifyError = (
  isActuallyCorrect: boolean,
  lastReversalTrial: number | null,
  currentTrialNumber: number,
  errorsSinceReversal: number,
  nextTrialWillBeCorrect: boolean // Lookahead for final reversal error
) => {
  if (isActuallyCorrect) {
    return 'none';
  }
  
  // Only classify errors after a reversal has occurred
  if (lastReversalTrial === null) {
    return 'random'; // Error before any reversal
  }
  
  const trialsSinceReversal = currentTrialNumber - lastReversalTrial;
  
  // REVERSAL ERROR: Immediately after reversal (first error)
  if (trialsSinceReversal === 1 && errorsSinceReversal === 0) {
    setTotalReversalErrors(prev => prev + 1);
    return 'reversal';
  }
  
  // PERSEVERATIVE ERROR: Continuing with old pattern
  if (trialsSinceReversal > 1 && errorsSinceReversal > 0) {
    setTotalPerseverativeErrors(prev => prev + 1);
    
    // FINAL REVERSAL ERROR: Last error before participant adapts
    // This requires lookahead - mark retroactively if next trial is correct
    if (shouldMarkAsFinalReversalError) {
      setTotalFinalReversalErrors(prev => prev + 1);
      return 'final_reversal';
    }
    
    return 'perseverative';
  }
  
  return 'random';
};
```

**Final Reversal Error Detection:**
Since we can't predict the future, use this approach:

```typescript
// Store last perseverative error index
let lastPerseverativeErrorIndex: number | null = null;

// On error
if (errorType === 'perseverative') {
  lastPerseverativeErrorIndex = currentTrialNumber;
}

// On correct response after reversal
if (isActuallyCorrect && lastReversalTrial && lastPerseverativeErrorIndex) {
  // Mark last perseverative error as "final reversal error"
  updateTrialData(lastPerseverativeErrorIndex, { 
    errorType: 'final_reversal' 
  });
  setTotalFinalReversalErrors(prev => prev + 1);
  setTotalPerseverativeErrors(prev => prev - 1); // Adjust count
  lastPerseverativeErrorIndex = null;
}
```

### 6.4 Shift Tracking Logic (CORRECTED)

**Purpose:** Measure behavioral flexibility

**Definitions:**
- **Win-Shift:** Change choice after receiving reward
- **Lose-Shift:** Change choice after receiving punishment

**Corrected Implementation:**

```typescript
const trackShifts = (
  currentChosenStimulus: string,
  currentDisplayedOutcome: 'reward' | 'punishment',
  lastChosenStimulus: string | null,
  lastDisplayedOutcome: 'reward' | 'punishment' | null
) => {
  // First trial has no previous to compare
  if (!lastChosenStimulus || !lastDisplayedOutcome) {
    // Store current for next trial
    setLastChosenStimulus(currentChosenStimulus);
    setLastDisplayedOutcome(currentDisplayedOutcome);
    
    // Count current outcome
    if (currentDisplayedOutcome === 'reward') {
      setTotalWins(prev => prev + 1);
    } else {
      setTotalLosses(prev => prev + 1);
    }
    return;
  }
  
  // Check if participant shifted choice
  const didShift = currentChosenStimulus !== lastChosenStimulus;
  
  // Classify based on PREVIOUS trial's outcome
  if (lastDisplayedOutcome === 'reward') {
    if (didShift) {
      setWinShifts(prev => prev + 1);
    }
    // Implicit: if !didShift, it's a "win-stay"
  } else if (lastDisplayedOutcome === 'punishment') {
    if (didShift) {
      setLoseShifts(prev => prev + 1);
    }
    // Implicit: if !didShift, it's a "lose-stay"
  }
  
  // Count current outcome for next trial
  if (currentDisplayedOutcome === 'reward') {
    setTotalWins(prev => prev + 1);
  } else {
    setTotalLosses(prev => prev + 1);
  }
  
  // Store current for next trial
  setLastChosenStimulus(currentChosenStimulus);
  setLastDisplayedOutcome(currentDisplayedOutcome);
};
```

**Calculation Formula:**
```typescript
const winShiftRate = totalWins > 0 ? (winShifts / totalWins) * 100 : 0;
const loseShiftRate = totalLosses > 0 ? (loseShifts / totalLosses) * 100 : 0;
```

**Interpretation:**
- **Low Win-Shift Rate (good):** Participant sticks with winning choices
- **High Lose-Shift Rate (good):** Participant adapts after mistakes
- **High Win-Shift Rate (poor):** Random/impulsive behavior
- **Low Lose-Shift Rate (poor):** Cognitive rigidity

### 6.5 Timeout Handling (CORRECTED)

```typescript
const handleTimeout = () => {
  // Clear timer
  clearInterval(timerRef.current);
  
  // Show timeout message
  setShowTimeout(true);
  
  // CRITICAL: Deduct score (was missing in prototype)
  if (ageGroup === 'adolescent') {
    setScore(prev => prev - 40);
  }
  
  // Record as incorrect response
  const trialData: TrialRecord = {
    participantChoice: 'timeout',
    responseAccuracy: 0,
    responseTime: 4000,
    errorType: 'random',
    scoreChange: -40,
    feedbackGiven: 'Time is up! -40 coins',
    // ... other fields
  };
  
  recordTrial(trialData);
  
  // Reset consecutive correct counter
  setConsecutiveCorrectCount(0);
  
  // Show timeout for 1 second, then proceed
  setTimeout(() => {
    showFixationAndProceed();
  }, 1000);
};
```

### 6.6 Forced Reversal Between Blocks (CONDITIONAL)

**Rule from PDF:** If participant achieves first reversal but not second reversal before block ends, next block can start with forced reversal.

```typescript
const handleBlockCompletion = (currentBlock: number, lastReversalTrial: number | null) => {
  const blockStartTrial = currentBlock * 12;
  const blockEndTrial = blockStartTrial + 11;
  
  // Check if reversal occurred in this block
  const reversalOccurredInBlock = lastReversalTrial && 
    lastReversalTrial >= blockStartTrial && 
    lastReversalTrial <= blockEndTrial;
  
  // Check if participant adapted to reversal (got 3 correct on new rule)
  const adaptedToReversal = consecutiveCorrectCount >= 3;
  
  // Scenario 3: Reversal occurred but not adapted
  if (reversalOccurredInBlock && !adaptedToReversal) {
    // Next block should start with FORCED reversal
    // Meaning: keep current correct side (don't flip)
    return { shouldForceReversal: false }; // Don't flip side
  }
  
  // Default: Start next block with natural initial correct side
  // Flip to opposite of current
  return { shouldForceReversal: true }; // Flip side
};

// At block transition
const startNewBlock = (nextBlockNumber: number) => {
  const { shouldForceReversal } = handleBlockCompletion(currentBlock, lastReversalTrial);
  
  if (shouldForceReversal) {
    setCurrentCorrectSide(prev => prev === 'left' ? 'right' : 'left');
  }
  // else: keep current side
  
  setCurrentBlock(nextBlockNumber);
  setCurrentRound(0);
  setConsecutiveCorrectCount(0);
  // ... other resets
};
```

---

## 7. Data Collection & Recording

### 7.1 Trial-Level Data Recording

**When to Record:** After EVERY trial (including timeouts)

```typescript
const recordTrialData = (trialInfo: Partial<TrialRecord>) => {
  const completeRecord: TrialRecord = {
    // Auto-generated
    trialNumber: testData.length + 1,
    blockNumber: currentScreen === 'practice' ? 'Practice' : currentBlock + 1,
    roundInBlock: currentRound + 1,
    timestamp: Date.now(),
    
    // From trial state
    stimulusSet: getCurrentStimulusSetName(),
    leftStimulus: leftStimulusPath,
    rightStimulus: rightStimulusPath,
    taskRule: `${getCurrentCorrectStimulusName()} is rewarded`,
    currentCorrectStimulus: getCurrentCorrectStimulusName(),
    
    // From user action
    ...trialInfo
  };
  
  setTestData(prev => [...prev, completeRecord]);
};
```

### 7.2 Block-Level Summary

**After Each Block, Calculate:**
```typescript
interface BlockSummary {
  blockNumber: number;
  accuracy: number;
  avgResponseTime: number;
  reversalsTriggered: number;
  reversalErrors: number;
  perseverativeErrors: number;
  timeouts: number;
}

const generateBlockSummary = (blockNumber: number, trialData: TrialRecord[]) => {
  const blockTrials = trialData.filter(t => t.blockNumber === blockNumber);
  
  return {
    blockNumber,
    accuracy: (blockTrials.filter(t => t.responseAccuracy === 1).length / 12) * 100,
    avgResponseTime: blockTrials.reduce((sum, t) => sum + t.responseTime, 0) / 12,
    reversalsTriggered: blockTrials.filter(t => t.reversalTriggered).length,
    // ... other metrics
  };
};
```

### 7.3 Data Export Functionality

**Export Formats:** CSV and JSON

```typescript
// utils/dataExport.ts
export const exportToCSV = (trialData: TrialRecord[], filename: string) => {
  const headers = Object.keys(trialData[0]).join(',');
  const rows = trialData.map(trial => 
    Object.values(trial).map(v => 
      typeof v === 'string' ? `"${v}"` : v
    ).join(',')
  );
  
  const csv = [headers, ...rows].join('\n');
  const blob = new Blob([csv], { type: 'text/csv' });
  const url = URL.createObjectURL(blob);
  
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.click();
};

export const exportToJSON = (trialData: TrialRecord[], summary: any, filename: string) => {
  const exportData = {
    metadata: {
      testName: 'Cognitive Flexibility Test',
      version: '1.0',
      exportDate: new Date().toISOString(),
      totalTrials: trialData.length
    },
    summary,
    trialData
  };
  
  const json = JSON.stringify(exportData, null, 2);
  const blob = new Blob([json], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.click();
};
```

### 7.4 Local Storage Backup

**Auto-save Progress:**
```typescript
useEffect(() => {
  // Save state every trial
  const stateBackup = {
    currentScreen,
    currentBlock,
    currentRound,
    score,
    trialData,
    timestamp: Date.now()
  };
  
  localStorage.setItem('cognitive-test-backup', JSON.stringify(stateBackup));
}, [trialData]);

// On mount, check for backup
useEffect(() => {
  const backup = localStorage.getItem('cognitive-test-backup');
  if (backup) {
    const parsed = JSON.parse(backup);
    // Check if backup is recent (within 1 hour)
    if (Date.now() - parsed.timestamp < 3600000) {
      // Offer to resume
      setShowResumePrompt(true);
    }
  }
}, []);
```

---

## 8. Timing & Performance

### 8.1 Timer Implementation

**Use `useRef` and `setInterval` for Accuracy:**

```typescript
const useTimer = (duration: number, onComplete: () => void) => {
  const [timeRemaining, setTimeRemaining] = useState(duration);
  const timerRef = useRef<NodeJS.Timeout | null>(null);
  const startTimeRef = useRef<number | null>(null);
  
  const start = () => {
    startTimeRef.current = Date.now();
    let elapsed = 0;
    
    timerRef.current = setInterval(() => {
      elapsed = Math.floor((Date.now() - startTimeRef.current!) / 1000);
      const remaining = duration - elapsed;
      
      setTimeRemaining(remaining);
      
      if (remaining <= 0) {
        stop();
        onComplete();
      }
    }, 100); // Update every 100ms for smooth display
  };
  
  const stop = () => {
    if (timerRef.current) {
      clearInterval(timerRef.current);
      timerRef.current = null;
    }
  };
  
  const getElapsedTime = () => {
    if (!startTimeRef.current) return 0;
    return Date.now() - startTimeRef.current;
  };
  
  useEffect(() => {
    return () => stop(); // Cleanup
  }, []);
  
  return { timeRemaining, start, stop, getElapsedTime };
};
```

### 8.2 Response Time Measurement

**Use High-Precision Timestamps:**

```typescript
const handleTrialStart = () => {
  const trialStartTime = performance.now(); // High precision
  setTrialStartTime(trialStartTime);
};

const handleChoice = (side: 'left' | 'right') => {
  const responseTime = performance.now() - trialStartTime;
  
  // Record in milliseconds with decimal precision
  recordTrial({ responseTime: Math.round(responseTime) });
};
```

### 8.3 Preventing Timer Drift

**Issue:** JavaScript timers can drift over many trials

**Solution:**
```typescript
// Calculate total expected time vs actual time
const totalExpectedTime = trialNumber * TIMING_CONFIG.TOTAL_TRIAL;
const actualElapsedTime = Date.now() - testStartTime;
const drift = actualElapsedTime - totalExpectedTime;

// Log drift for debugging
if (drift > 1000) { // More than 1 second drift
  console.warn(`Timer drift detected: ${drift}ms`);
}
```

### 8.4 Performance Optimization

**Image Optimization:**
- Preload next block images during current block
- Use WebP format with PNG fallback
- Lazy load images for results screen

**Render Optimization:**
```typescript
// Memoize expensive calculations
const blockSummary = useMemo(() => 
  calculateBlockSummary(trialData), 
  [trialData]
);

// Prevent unnecessary re-renders
const StimulusDisplay = React.memo(({ src, alt, onClick }) => {
  // ...
});
```

**State Updates:**
```typescript
// Batch state updates when possible
setGameState(prev => ({
  ...prev,
  currentRound: prev.currentRound + 1,
  showFeedback: false,
  showFixation: false
}));
```

---

## 9. Error Handling & Edge Cases

### 9.1 Image Loading Failures

```typescript
const StimulusImage = ({ src, alt, onError }) => {
  const [error, setError] = useState(false);
  
  const handleImageError = () => {
    setError(true);
    console.error(`Failed to load image: ${src}`);
    onError?.(src);
  };
  
  if (error) {
    return (
      <div className="image-error">
        <span>⚠️ Image unavailable</span>
        <button onClick={() => window.location.reload()}>
          Reload Page
        </button>
      </div>
    );
  }
  
  return <img src={src} alt={alt} onError={handleImageError} />;
};
```

### 9.2 Browser Tab Visibility

**Pause test when tab is hidden:**

```typescript
useEffect(() => {
  const handleVisibilityChange = () => {
    if (document.hidden) {
      // Pause timers
      pauseTest();
      showWarning('Test paused. Please return to this tab.');
    } else {
      // Resume timers
      resumeTest();
    }
  };
  
  document.addEventListener('visibilitychange', handleVisibilityChange);
  return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
}, []);
```

### 9.3 Rapid Double-Clicks

**Prevent multiple responses:**

```typescript
const handleChoice = (side: 'left' | 'right') => {
  // Check if already responded
  if (selectedSide !== null || showFeedback) {
    return; // Ignore duplicate clicks
  }
  
  // Disable further clicks
  setSelectedSide(side);
  
  // Process response
  // ...
};
```

### 9.4 Browser Refresh/Close Warning

```typescript
useEffect(() => {
  const handleBeforeUnload = (e: BeforeUnloadEvent) => {
    if (currentScreen === 'practice' || currentScreen === 'main') {
      e.preventDefault();
      e.returnValue = 'Your progress will be lost. Are you sure?';
      return e.returnValue;
    }
  };
  
  window.addEventListener('beforeunload', handleBeforeUnload);
  return () => window.removeEventListener('beforeunload', handleBeforeUnload);
}, [currentScreen]);
```

### 9.5 Network Interruption (Future API)

```typescript
const fetchStimulusImage = async (url: string) => {
  try {
    const response = await fetch(url, { 
      timeout: 5000,
      retry: 3 
    });
    return await response.blob();
  } catch (error) {
    // Fallback to local image
    console.error('API fetch failed, using local fallback');
    return loadLocalImage(url);
  }
};
```

### 9.6 Invalid State Recovery

```typescript
const validateState = (state: TestState) => {
  const issues: string[] = [];
  
  if (state.currentBlock < 0 || state.currentBlock > 5) {
    issues.push('Invalid block number');
  }
  
  if (state.currentRound < 0 || state.currentRound > 11) {
    issues.push('Invalid round number');
  }
  
  if (state.consecutiveCorrectCount < 0) {
    issues.push('Invalid consecutive correct count');
  }
  
  if (issues.length > 0) {
    console.error('State validation failed:', issues);
    // Reset to safe state
    return getDefaultState();
  }
  
  return state;
};
```

---

**Practice Phase:**
- [ ] 12 rounds complete (not 8)
- [ ] No reversals occur even after 3+ consecutive correct
- [ ] Probabilistic feedback appears ~3 times
- [ ] Timeout handled correctly with score deduction
- [ ] "Practice Complete" screen shows

**Main Test:**
- [ ] 6 blocks × 12 rounds = 72 total
- [ ] Block instructions appear between blocks
- [ ] Reversals trigger after 3 consecutive correct
- [ ] Images load correctly for each block
- [ ] Score updates correctly (adolescents)
- [ ] Blue outline appears on selection
- [ ] Timer counts down from 4 seconds

**Results Screen:**
- [ ] All metrics calculated correctly
- [ ] Win-shift and lose-shift rates accurate
- [ ] Error counts match manual count
- [ ] Export data works (CSV/JSON)

**Edge Cases:**
- [ ] Rapid clicking doesn't cause issues
- [ ] Tab switching pauses test
- [ ] Browser refresh warns user
- [ ] Image load failures handled gracefully

---

## 11. Future API Integration

### 11.1 API Endpoints (Planned)

**Authentication:**
```
POST /api/auth/login
POST /api/auth/register
```

**Test Management:**
```
GET  /api/tests                    // List available tests
GET  /api/tests/{testId}            // Get test configuration
POST /api/tests/{testId}/start      // Initialize test session
```

**Stimulus Fetching:**
```
GET  /api/stimuli/{ageGroup}/{blockType}/{stimulusName}
```

**Data Submission:**
```
POST /api/tests/{testId}/sessions/{sessionId}/trials    // Submit trial data
POST /api/tests/{testId}/sessions/{sessionId}/complete  // Submit complete results
```

**Results Retrieval:**
```
GET  /api/tests/{testId}/sessions/{sessionId}/results
GET  /api/users/{userId}/test-history
```

### 11.2 API Service Layer

```typescript
// services/api.service.ts
export class CognitiveTestAPI {
  private baseURL: string;
  private sessionId: string | null = null;
  
  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }
  
  async startSession(testId: string, userId: string): Promise<string> {
    const response = await fetch(`${this.baseURL}/tests/${testId}/start`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId })
    });
    
    const data = await response.json();
    this.sessionId = data.sessionId;
    return this.sessionId;
  }
  
  async fetchStimulus(path: string): Promise<string> {
    // Try API first, fallback to local
    try {
      const response = await fetch(`${this.baseURL}/stimuli${path}`);
      const blob = await response.blob();
      return URL.createObjectURL(blob);
    } catch (error) {
      console.warn('API fetch failed, using local image');
      return path; // Return local path
    }
  }
  
  async submitTrial(trialData: TrialRecord): Promise<void> {
    await fetch(`${this.baseURL}/tests/${testId}/sessions/${this.sessionId}/trials`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(trialData)
    });
  }
  
  async submitResults(results: any): Promise<void> {
    await fetch(`${this.baseURL}/tests/${testId}/sessions/${this.sessionId}/complete`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(results)
    });
  }
}
```

### 11.3 Environment Configuration

```typescript
// config/environment.ts
export const config = {
  API_URL: process.env.REACT_APP_API_URL || 'http://localhost:3000/api',
  USE_LOCAL_IMAGES: process.env.REACT_APP_USE_LOCAL_IMAGES === 'true',
  ENABLE_API: process.env.REACT_APP_ENABLE_API === 'true',
  AUTO_SAVE_INTERVAL: 30000, // 30 seconds
};
```

### 11.4 Offline Support

```typescript
// utils/offlineQueue.ts
class OfflineQueue {
  private queue: any[] = [];
  
  add(request: any) {
    this.queue.push(request);
    this.saveToStorage();
  }
  
  async flush() {
    while (this.queue.length > 0) {
      const request = this.queue[0];
      try {
        await this.sendRequest(request);
        this.queue.shift();
      } catch (error) {
        console.error('Failed to flush request:', error);
        break; // Stop if still offline
      }
    }
    this.saveToStorage();
  }
  
  private saveToStorage() {
    localStorage.setItem('offline-queue', JSON.stringify(this.queue));
  }
  
  restore() {
    const saved = localStorage.getItem('offline-queue');
    if (saved) {
      this.queue = JSON.parse(saved);
    }
  }
}
```

### 11.5 Real-time Progress Sync

```typescript
// For future implementation with WebSocket
class ProgressSync {
  private ws: WebSocket | null = null;
  
  connect(sessionId: string) {
    this.ws = new WebSocket(`ws://api.example.com/sessions/${sessionId}`);
    
    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      // Handle real-time updates (e.g., supervisor monitoring)
    };
  }
  
  sendProgress(trial: TrialRecord) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(trial));
    }
  }
}
```

---

## 12. Implementation Checklist

### 12.1 Phase 1: Core Implementation (Week 1-2)

- [ ] Set up project structure with TypeScript
- [ ] Implement state management architecture
- [ ] Create all screen components (Intro, Practice, Main, Results)
- [ ] Implement core game loop with correct timing
- [ ] Add reversal logic (disabled for practice)
- [ ] Implement probabilistic feedback system
- [ ] Create stimulus image display components
- [ ] Add timeout handling with score deduction

### 12.2 Phase 2: Data & Logic (Week 2-3)

- [ ] Implement trial data recording
- [ ] Add error classification (reversal, perseverative, final)
- [ ] Implement shift tracking (corrected logic)
- [ ] Add data export (CSV/JSON)
- [ ] Implement block transition logic
- [ ] Add conditional forced reversal between blocks
- [ ] Create results calculation functions

### 12.3 Phase 3: Assets & UI (Week 3)

- [ ] Design/source all stimulus images
- [ ] Organize images in public folder structure
- [ ] Implement age-specific UI (adolescent vs adult)
- [ ] Add animations and transitions
- [ ] Create feedback displays (coins vs smileys)
- [ ] Implement responsive design (345px - large screens)
- [ ] Add loading states and error handling

### 12.4 Phase 4: Testing & Polish (Week 4)

- [ ] Write unit tests for core logic
- [ ] Write integration tests for game flow
- [ ] Perform manual testing with checklist
- [ ] Test on multiple devices/browsers
- [ ] Optimize performance
- [ ] Add accessibility features (ARIA labels)
- [ ] Create user documentation

### 12.5 Phase 5: Future Preparation (Ongoing)

- [ ] Design API integration layer
- [ ] Implement offline queue system
- [ ] Add environment configuration
- [ ] Create API service abstraction
- [ ] Document API requirements for backend team
- [ ] Plan authentication flow

---

| Issue | Prototype | Corrected Implementation |
|-------|-----------|-------------------------|
| **Test Length** | 24 rounds (3×8) | 72 rounds (6×12) |
| **Practice Rounds** | 8 rounds | 12 rounds |
| **Practice Reversals** | Enabled | DISABLED |
| **Stimuli** | Generic emojis | Age-specific images |
| **Shift Tracking** | Off by one trial | Correct timing |
| **Timeout Score** | No deduction | -40 coins deducted |
| **Final Reversal Error** | Not tracked | Properly tracked |
| **Block Instructions** | Missing | Added between blocks |
| **Forced Reversal** | Always | Conditional (Scenario 3) |
| **Data Fields** | Basic | Complete per PDF spec |

---

## 14. Conclusion

This implementation plan provides a comprehensive roadmap for building the Cognitive Flexibility Test frontend application that strictly adheres to the research specifications. By following this documentation:

1. **All critical issues from the prototype are addressed**
2. **Exact test specifications are implemented**
3. **Data collection meets research standards**
4. **System is prepared for future API integration**
5. **Code is maintainable and testable**

The modular architecture ensures that each component can be developed, tested, and debugged independently while maintaining the integrity of the overall system.

---

### State Management Summary

- **Navigation States:** intro → practice → practiceComplete → main → blockInstruction → results
- **Core Counters:** currentBlock (0-5), currentRound (0-11)
- **Reversal Tracking:** consecutiveCorrectCount, reversalCount, lastReversalTrial
- **Error Tracking:** reversalErrors, perseverativeErrors, finalReversalErrors
- **Shift Tracking:** winShifts, loseShifts, totalWins, totalLosses

---

**Document Version:** 1.0  
**Last Updated:** October 30, 2025  
**Author:** AI Assistant  
**Status:** Ready for Implementation

