import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.math.*;
public class main {
public static void main(String[] args) throws Exception {
  String s = structure(new int[] {123, -123});
  print(s);
  int[] b = (int[]) unstructure(s);
  String s2 = structure(b);
  assertEquals(s, s2);
  print("ok");
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static volatile StringBuffer print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
//static int print_maxLineLength = 0; // 0 = unset
static boolean print_silent; // total mute if set
static void print() {
  print("");
}
// slightly overblown signature to return original object...
static  A print(A o) {
  if (print_silent) return o;
  String s = String.valueOf(o) + "\n";
  // TODO if (print_maxLineLength != 0)
  StringBuffer loc = local_log;
  StringBuffer buf = print_log;
  int loc_max = print_log_max;
  if (buf != loc && buf != null) {
    print_append(buf, s, print_log_max);
    loc_max = local_log_max;
  }
  if (loc != null) 
    print_append(loc, s, loc_max);
  System.out.print(s);
  return o;
}
static void print(long l) {
  print(String.valueOf(l));
}
static void print(char c) {
  print(String.valueOf(c));
}
static void print_append(StringBuffer buf, String s, int max) {
  synchronized(buf) {
    buf.append(s);
    max /= 2;
    if (buf.length() > max) try {
      int newLength = max/2;
      int ofs = buf.length()-newLength;
      String newString = buf.substring(ofs);
      buf.setLength(0);
      buf.append("[...] ").append(newString);
    } catch (Exception e) {
      buf.setLength(0);
    }
  }
}
static  A assertEquals(Object x, A y) {
  return assertEquals(null, x, y);
}
static  A assertEquals(String msg, Object x, A y) {
  if (!(x == null ? y == null : x.equals(y)))
    throw fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y));
  return y;
}
static Object unstructure(String text) {
  return unstructure(text, false);
}
static Object unstructure(String text, final boolean allDynamic) {
  return unstructure(text, allDynamic, null);
}
static int structure_internStringsLongerThan = 50;
// TODO: backrefs for hashmap{} etc
// classFinder: name -> class
static Object unstructure(String text, final boolean allDynamic,
  final Object classFinder) {
  if (text == null) return null;
  final List tok = javaTok(text);
  final boolean debug = unstructure_debug;
  
  class X {
    int i = 1;
    HashMap refs = new HashMap();
    HashSet concepts = new HashSet();
    Object parse() {
      String t = tok.get(i);
      
      int refID = 0;
      if (t.startsWith("m") && isInteger(t.substring(1))) {
        refID = parseInt(t.substring(1));
        i += 2;
        t = tok.get(i);
      }
      
      if (debug)
        print("parse: " + quote(t));
        
      if (t.startsWith("'")) {
        char c = unquoteCharacter(tok.get(i));
        i += 2;
        return c;
      }
      if (t.equals("bigint"))
        return parseBigInt();
      if (t.equals("d"))
        return parseDouble();
      if (t.equals("fl"))
        return parseFloat();
      if (t.equals("false") || t.equals("f")) {
        i += 2; return false;
      }
      if (t.equals("true") || t.equals("t")) {
        i += 2; return true;
      }
      if (t.equals("-")) {
        t = tok.get(i+2);
        i += 4;
        return isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t));
      }
      if (isInteger(t) || isLongConstant(t)) {
        i += 2;
        if (debug)
          print("isLongConstant " + quote(t) + " => " + isLongConstant(t));
        if (isLongConstant(t)) return parseLong(t);
        long l = parseLong(t);
        boolean isInt = l == (int) l;
        if (debug)
          print("l=" + l + ", isInt: " + isInt);
        return isInt ? (Object) new Integer((int) l) : (Object) new Long(l);
      }
      
      if (t.equals("File")) {
        File f = new File(unquote(tok.get(i+2)));
        i += 4;
        return f;
      }
      
      if (t.startsWith("r") && isInteger(t.substring(1))) {
        i += 2;
        int ref = Integer.parseInt(t.substring(1));
        Object o = refs.get(ref);
        if (o == null)
          print("Warning: unsatisfied back reference " + ref);
        return o;
      }
      
      Object o = parse_inner(refID);
      if (refID != 0)
        refs.put(refID, o);
      return o;
    }
    
    // everything that can be backreferenced
    Object parse_inner(int refID) {
      String t = tok.get(i);
      
      if (debug)
        print("parse_inner: " + quote(t));
        
      if (t.startsWith("\"")) {
        String s = internIfLongerThan(unquote(tok.get(i)), structure_internStringsLongerThan);
        i += 2;
        return s;
      }
      if (t.equals("hashset"))
        return parseHashSet();
      if (t.equals("treeset"))
        return parseTreeSet();
      if (eqOneOf(t, "hashmap", "hm"))
        return parseHashMap();
      if (t.equals("{"))
        return parseMap();
      if (t.equals("["))
        return parseList();
      if (t.equals("array") || t.equals("intarray"))
        return parseArray();
      if (t.equals("ba")) {
        String hex = unquote(tok.get(i+2));
        i += 4;
        return hexToBytes(hex);
      }
      if (t.equals("boolarray")) {
        int n = parseInt(tok.get(i+2));
        String hex = unquote(tok.get(i+4));
        i += 6;
        return boolArrayFromBytes(hexToBytes(hex), n);
      }
      if (t.equals("class"))
        return parseClass();
      if (t.equals("l"))
        return parseLisp();
      if (t.equals("null")) {
        i += 2; return null;
      }
      
      /* in dev.
      if (!allDynamic && t.equals("run")) {
        S snippetID = unquote(t.get(i+2));
        i += 4;
        run(
      }
      */
      
      boolean concept = eq(t, "c");
      if (concept) {
        consume("c");
        t = tok.get(i);
        assertTrue(isJavaIdentifier(t));
        concepts.add(t);
      }
      // any other class name
      if (isJavaIdentifier(t)) {
        // First, find class
        Class c;
        if (allDynamic) c = null;
        else if (classFinder != null)
          c = (Class) callF(classFinder, t);
        else
          c = findClass(t);
          
        // Second, check if it has an outer reference
        i += 2;
        boolean hasOuter = eq(get(tok, i), "(") && eq(get(tok, i+2), "this$1");
        
        DynamicObject dO = null;
        Object o = null;
        if (c != null)
          o = hasOuter ? nuStubInnerObject(c) : nuObject(c);
        else {
          if (concepts.contains(t) && (c = findClass("Concept")) != null)
            dO = (DynamicObject) nuObject(c);
          else
            dO = new DynamicObject();
          dO.className = t;
        }
        
        // Now, save in references list.
        
        if (refID != 0)
          refs.put(refID, o != null ? o : dO);
        
        // NOW parse the fields!
        
        Map fields = new TreeMap();
        if (i < tok.size() && tok.get(i).equals("(")) {
          consume("(");
          while (!tok.get(i).equals(")")) {
            String key = unquote(tok.get(i));
            i += 2;
            consume("=");
            Object value = parse();
            fields.put(key, value);
            if (tok.get(i).equals(",")) i += 2;
          }
          consume(")");
        }
        if (o != null)
          setOptAll(o, fields);
        else
          dO.fieldValues.putAll(fields);
        if (o != null)
          pcallOpt(o, "_doneLoading");
        return o != null ? o : dO;
      }
      throw new RuntimeException("Unknown token " + (i+1) + ": " + t);
    }
    
    Object parseSet(Set set) {
      set.addAll((List) parseList());
      return set;
    }
    
    Object parseLisp() {
      consume("l");
      consume("(");
      List list = new ArrayList();
      while (!tok.get(i).equals(")")) {
        list.add(parse());
        if (tok.get(i).equals(",")) i += 2;
      }
      consume(")");
      return newObject("main$Lisp", (String) list.get(0), subList(list, 1));
    }
    
    Object parseList() {
      consume("[");
      List list = new ArrayList();
      while (!tok.get(i).equals("]")) {
        Object o = parse();
        if (debug)
          print("List element type: " + getClassName(o));
        list.add(o);
        if (tok.get(i).equals(",")) i += 2;
      }
      consume("]");
      return list;
    }
    
    Object parseArray() {
      String type = tok.get(i);
      i += 2;
      consume("{");
      List list = new ArrayList();
      
      while (!tok.get(i).equals("}")) {
        list.add(parse());
        if (tok.get(i).equals(",")) i += 2;
      }
      consume("}");
      if (type.equals("intarray"))
        return toIntArray(list);
      return list.toArray();
    }
    
    Object parseClass() {
      consume("class");
      consume("(");
      String name = tok.get(i);
      i += 2;
      consume(")");
      Class c = allDynamic ? null : findClass(name);
      if (c != null) return c;
      DynamicObject dO = new DynamicObject();
      dO.className = "java.lang.Class";
      dO.fieldValues.put("name", name);
      return dO;
    }
    
    Object parseBigInt() {
      consume("bigint");
      consume("(");
      String val = tok.get(i);
      i += 2;
      if (eq(val, "-")) {
        val = "-" + tok.get(i);
        i += 2;
      }
      consume(")");
      return new BigInteger(val);
    }
    
    Object parseDouble() {
      consume("d");
      consume("(");
      String val = unquote(tok.get(i));
      i += 2;
      consume(")");
      return Double.parseDouble(val);
    }
    
    Object parseFloat() {
      consume("fl");
      String val;
      if (eq(tok.get(i), "(")) {
        consume("(");
        val = unquote(tok.get(i));
        i += 2;
        consume(")");
      } else {
        val = unquote(tok.get(i));
        i += 2;
      }
      return Float.parseFloat(val);
    }
    
    Object parseHashMap() {
      i += 2;
      return parseMap(new HashMap());
    }
    
    Object parseHashSet() {
      consume("hashset");
      return parseSet(new HashSet());
    }
    
    Object parseTreeSet() {
      consume("treeset");
      return parseSet(new TreeSet());
    }
    
    Object parseMap() {
      return parseMap(new TreeMap());
    }
    
    Object parseMap(Map map) {
      consume("{");
      while (!tok.get(i).equals("}")) {
        Object key = parse();
        consume("=");
        Object value = parse();
        map.put(key, value);
        if (tok.get(i).equals(",")) i += 2;
      }
      consume("}");
      return map;
    }
    
    void consume(String s) {
      if (!tok.get(i).equals(s)) {
        String prevToken = i-2 >= 0 ? tok.get(i-2) : "";
        String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size())));
        throw fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");
      }
      i += 2;
    }
  }
  
  return new X().parse();
}
static boolean unstructure_debug;
static String structure(Object o) {
  HashSet refd = new HashSet();
  return structure_2(structure_1(o, new structure_Data(refd)), refd);
}
// leave to false, unless unstructure() breaks
static boolean structure_allowShortening = false;
static int structure_shareStringsLongerThan = 20;
static class structure_Data {
  int stringSizeLimit;
  IdentityHashMap