text: # -*- coding: utf-8 -*-
  r"""
  Attributes of Definitions
  
  While a definition like 'cube[$x_$] = $x$^3' gives a way to specify <em>values</em> of a function, <em>attributes</em> allow a way to specify general properties of functions and symbols. This is independent of the parameters they take and the values they produce.
  
  The builtin-attributes having a predefined meaning in \Mathics which are described below.
  
  However in contrast to \Mathematica, you can set any symbol as an attribute.
  """
  
  from mathics.version import __version__  # noqa used in loading to check consistency.
  
  from mathics.builtin.base import Predefined, Builtin
  from mathics.core.expression import Expression, Symbol, SymbolNull, String
  from mathics.builtin.assignment import get_symbol_list
  
  
  class Attributes(Builtin):
      """
      <dl>
        <dt>'Attributes'[$symbol$]
        <dd>returns the attributes of $symbol$.
  
        <dt>'Attributes'["$string$"]
        <dd>returns the attributes of 'Symbol'["$string$"].
  
        <dt>'Attributes'[$symbol$] = {$attr1$, $attr2$}
        <dd>sets the attributes of $symbol$, replacing any existing attributes.
      </dl>
  
      >> Attributes[Plus]
       = {Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
  
      >> Attributes["Plus"]
       = {Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
  
      'Attributes' always considers the head of an expression:
      >> Attributes[a + b + c]
       = {Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
  
      You can assign values to 'Attributes' to set attributes:
  
      >> Attributes[f] = {Flat, Orderless}
       = {Flat, Orderless}
      >> f[b, f[a, c]]
       = f[a, b, c]
      Attributes must be symbols:
      >> Attributes[f] := {a + b}
       : Argument a + b at position 1 is expected to be a symbol.
       = $Failed
      Use 'Symbol' to convert strings to symbols:
      >> Attributes[f] = Symbol["Listable"]
       = Listable
      >> Attributes[f]
       = {Listable}
      """
  
      attributes = ("HoldAll", "Listable")
  
      def apply(self, expr, evaluation):
          "Attributes[expr_]"
  
          if isinstance(expr, String):
              expr = Symbol(expr.get_string_value())
          name = expr.get_lookup_name()
          attributes = list(evaluation.definitions.get_attributes(name))
          attributes.sort()
          attr = [Symbol(attribute) for attribute in attributes]
          return Expression("List", *attr)
  
  
  class SetAttributes(Builtin):
      """
      <dl>
      <dt>'SetAttributes'[$symbol$, $attrib$]
          <dd>adds $attrib$ to the list of $symbol$'s attributes.
      </dl>
  
      >> SetAttributes[f, Flat]
      >> Attributes[f]
       = {Flat}
  
      Multiple attributes can be set at the same time using lists:
      >> SetAttributes[{f, g}, {Flat, Orderless}]
      >> Attributes[g]
       = {Flat, Orderless}
      """
  
      attributes = ("HoldFirst",)
  
      def apply(self, symbols, attributes, evaluation):
          "SetAttributes[symbols_, attributes_]"
  
          symbols = get_symbol_list(
              symbols, lambda item: evaluation.message("SetAttributes", "sym", item, 1)
          )
          if symbols is None:
              return
          values = get_symbol_list(
              attributes, lambda item: evaluation.message("SetAttributes", "sym", item, 2)
          )
          if values is None:
              return
          for symbol in symbols:
              if "System`Locked" in evaluation.definitions.get_attributes(symbol):
                  evaluation.message("SetAttributes", "locked", Symbol(symbol))
              else:
                  for value in values:
                      evaluation.definitions.set_attribute(symbol, value)
          return SymbolNull
  
  
  class ClearAttributes(Builtin):
      """
      <dl>
      <dt>'ClearAttributes'[$symbol$, $attrib$]
          <dd>removes $attrib$ from $symbol$'s attributes.
      </dl>
  
      >> SetAttributes[f, Flat]
      >> Attributes[f]
       = {Flat}
      >> ClearAttributes[f, Flat]
      >> Attributes[f]
       = {}
      Attributes that are not even set are simply ignored:
      >> ClearAttributes[{f}, {Flat}]
      >> Attributes[f]
       = {}
      """
  
      attributes = ("HoldFirst",)
  
      def apply(self, symbols, attributes, evaluation):
          "ClearAttributes[symbols_, attributes_]"
  
          symbols = get_symbol_list(
              symbols, lambda item: evaluation.message("ClearAttributes", "sym", item, 1)
          )
          if symbols is None:
              return
          values = get_symbol_list(
              attributes,
              lambda item: evaluation.message("ClearAttributes", "sym", item, 2),
          )
          if values is None:
              return
          for symbol in symbols:
              if "System`Locked" in evaluation.definitions.get_attributes(symbol):
                  evaluation.message("ClearAttributes", "locked", Symbol(symbol))
              else:
                  for value in values:
                      evaluation.definitions.clear_attribute(symbol, value)
          return SymbolNull
  
  
  class Protect(Builtin):
      """
      <dl>
        <dt>'Protect'[$s1$, $s2$, ...]
        <dd>sets the attribute 'Protected' for the symbols $si$.
  
        <dt>'Protect'[$str1$, $str2$, ...]
        <dd>protects all symbols whose names textually match $stri$.
      </dl>
  
      >> A = {1, 2, 3};
      >> Protect[A]
      >> A[[2]] = 4;
       : Symbol A is Protected.
      >> A
       = {1, 2, 3}
      """
  
      attributes = ("HoldAll",)
      messages = {
          "ssym": "`1` is not a symbol or a string.",
      }
  
      def apply(self, symbols, evaluation):
          "Protect[symbols___]"
          protected = Symbol("System`Protected")
          items = []
  
          if isinstance(symbols, Symbol):
              symbols = [symbols]
          elif isinstance(symbols, String):
              symbols = [symbols]
          elif isinstance(symbols, Expression):
              if symbols.get_head_name() in ("System`Sequence", "System`List"):
                  symbols = symbols.get_leaves()
              else:
                  evaluation.message("Protect", "ssym", symbols)
                  return SymbolNull
  
          for symbol in symbols:
              if isinstance(symbol, Symbol):
                  items.append(symbol)
              else:
                  pattern = symbol.get_string_value()
                  if not pattern or pattern == "":
                      evaluation.message("Protect", "ssym", symbol)
                      continue
  
                  if pattern[0] == "`":
                      pattern = evaluation.definitions.get_current_context() + pattern[1:]
                  names = evaluation.definitions.get_matching_names(pattern)
                  for defn in names:
                      symbol = Symbol(defn)
                      if not "System`Locked" in evaluation.definitions.get_attributes(
                          defn
                      ):
                          items.append(symbol)
  
          Expression("SetAttributes", Expression("List", *items), protected).evaluate(
              evaluation
          )
          return SymbolNull
  
  
  class Unprotect(Builtin):
      """
      <dl>
        <dt>'Unprotect'[$s1$, $s2$, ...]
        <dd>removes the attribute 'Protected' for the symbols $si$.
  
        <dt>'Unprotect'[$str$]
        <dd>unprotects symbols whose names textually match $str$.
      </dl>
      """
  
      attributes = ("HoldAll",)
      messages = {
          "ssym": "`1` is not a symbol or a string.",
      }
  
      def apply(self, symbols, evaluation):
          "Unprotect[symbols___]"
          protected = Symbol("System`Protected")
          items = []
          if isinstance(symbols, Symbol):
              symbols = [symbols]
          elif isinstance(symbols, Expression):
              symbols = symbols.get_leaves()
          elif isinstance(symbols, String):
              symbols = [symbols]
          else:
              symbols = symbols.get_sequence()
  
          for symbol in symbols:
              if isinstance(symbol, Symbol):
                  items.append(symbol)
              else:
                  pattern = symbol.get_string_value()
                  if not pattern or pattern == "":
                      evaluation.message("Unprotect", "ssym", symbol)
                      continue
  
                  if pattern[0] == "`":
                      pattern = evaluation.definitions.get_current_context() + pattern[1:]
                  names = evaluation.definitions.get_matching_names(pattern)
                  for defn in names:
                      symbol = Symbol(defn)
                      if not "System`Locked" in evaluation.definitions.get_attributes(
                          defn
                      ):
                          items.append(symbol)
  
          Expression("ClearAttributes", Expression("List", *items), protected).evaluate(
              evaluation
          )
          return SymbolNull
  
  
  class Protected(Predefined):
      """
      <dl>
      <dt>'Protected'
          <dd>is an attribute that prevents values on a symbol from
          being modified.
      </dl>
  
      Values of 'Protected' symbols cannot be modified:
      >> Attributes[p] = {Protected};
      >> p = 2;
       : Symbol p is Protected.
      >> f[p] ^= 3;
       : Tag p in f[p] is Protected.
      >> Format[p] = "text";
       : Symbol p is Protected.
  
      However, attributes might still be set:
      >> SetAttributes[p, Flat]
      >> Attributes[p]
       = {Flat, Protected}
      Thus, you can easily remove the attribute 'Protected':
      >> Attributes[p] = {};
      >> p = 2
       = 2
      You can also use 'Protect' or 'Unprotect', resp.
      >> Protect[p]
      >> Attributes[p]
       = {Protected}
      >> Unprotect[p]
  
      If a symbol is 'Protected' and 'Locked', it can never be changed again:
      >> SetAttributes[p, {Protected, Locked}]
      >> p = 2
       : Symbol p is Protected.
       = 2
      >> Unprotect[p]
       : Symbol p is locked.
      """
  
  
  class ReadProtected(Predefined):
      """
      <dl>
      <dt>'ReadProtected'
          <dd>is an attribute that prevents values on a symbol from
          being read.
      </dl>
  
      Values associated with 'ReadProtected' symbols cannot be seen in
      'Definition':
      >> ClearAll[p]
      >> p = 3;
      >> Definition[p]
       = p = 3
      >> SetAttributes[p, ReadProtected]
      >> Definition[p]
       = Attributes[p] = {ReadProtected}
      """
  
  
  class Locked(Predefined):
      """
      <dl>
      <dt>'Locked'
          <dd>is an attribute that prevents attributes on a symbol from
          being modified.
      </dl>
  
      The attributes of 'Locked' symbols cannot be modified:
      >> Attributes[lock] = {Flat, Locked};
      >> SetAttributes[lock, {}]
       : Symbol lock is locked.
      >> ClearAttributes[lock, Flat]
       : Symbol lock is locked.
      >> Attributes[lock] = {}
       : Symbol lock is locked.
       = {}
      >> Attributes[lock]
       = {Flat, Locked}
  
      However, their values might be modified (as long as they are not 'Protected' too):
      >> lock = 3
       = 3
      """
  
  
  class Flat(Predefined):
      """
      <dl>
      <dt>'Flat'
          <dd>is an attribute that specifies that nested occurrences of
          a function should be automatically flattened.
      </dl>
  
      A symbol with the 'Flat' attribute represents an associative
      mathematical operation:
      >> SetAttributes[f, Flat]
      >> f[a, f[b, c]]
       = f[a, b, c]
  
      'Flat' is taken into account in pattern matching:
      >> f[a, b, c] /. f[a, b] -> d
       = f[d, c]
  
      #> SetAttributes[{u, v}, Flat]
      #> u[x_] := {x}
      #> u[]
       = u[]
      #> u[a]
       = {a}
      #> u[a, b]
       : Iteration limit of 1000 exceeded.
       = $Aborted
      #> u[a, b, c]
       : Iteration limit of 1000 exceeded.
       = $Aborted
      #> v[x_] := x
      #> v[]
       = v[]
      #> v[a]
       = a
      #> v[a, b] (* in Mathematica: Iteration limit of 4096 exceeded. *)
       = v[a, b]
      #> v[a, b, c] (* in Mathematica: Iteration limit of 4096 exceeded. *)
       : Iteration limit of 1000 exceeded.
       = $Aborted
      """
  
  
  class Orderless(Predefined):
      """<dl>
          <dt>'Orderless'
          <dd>is an attribute that can be assigned to a symbol $f$ to
          indicate that the elements $ei$ in expressions of the form
          $f$[$e1$, $e2$, ...] should automatically be sorted into
          canonical order. This property is accounted for in pattern
          matching.
      </dl>
  
      The leaves of an 'Orderless' function are automatically sorted:
      >> SetAttributes[f, Orderless]
      >> f[c, a, b, a + b, 3, 1.0]
       = f[1., 3, a, b, c, a + b]
  
      A symbol with the 'Orderless' attribute represents a commutative
      mathematical operation.
      >> f[a, b] == f[b, a]
       = True
  
      'Orderless' affects pattern matching:
      >> SetAttributes[f, Flat]
      >> f[a, b, c] /. f[a, c] -> d
       = f[b, d]
  
      """
  
  
  class OneIdentity(Predefined):
      """
      <dl>
      <dt>'OneIdentity'
          <dd>is an attribute specifying that '$f$[$x$]' should be treated
          as equivalent to $x$ in pattern matching.
      </dl>
  
      'OneIdentity' affects pattern matching:
      >> SetAttributes[f, OneIdentity]
      >> a /. f[args___] -> {args}
       = {a}
      It does not affect evaluation:
      >> f[a]
       = f[a]
      """
  
  
  class SequenceHold(Predefined):
      """
      <dl>
      <dt>'SequenceHold'
          <dd>is an attribute that prevents 'Sequence' objects from being
          spliced into a function's arguments.
      </dl>
  
      Normally, 'Sequence' will be spliced into a function:
      >> f[Sequence[a, b]]
       = f[a, b]
      It does not for 'SequenceHold' functions:
      >> SetAttributes[f, SequenceHold]
      >> f[Sequence[a, b]]
       = f[Sequence[a, b]]
  
      E.g., 'Set' has attribute 'SequenceHold' to allow assignment of sequences to variables:
      >> s = Sequence[a, b];
      >> s
       = Sequence[a, b]
      >> Plus[s]
       = a + b
      """
  
  
  class HoldFirst(Predefined):
      """
      <dl>
      <dt>'HoldFirst'
          <dd>is an attribute specifying that the first argument of a
          function should be left unevaluated.
      </dl>
  
      >> Attributes[Set]
       = {HoldFirst, Protected, SequenceHold}
      """
  
  
  class HoldRest(Predefined):
      """
      <dl>
      <dt>'HoldRest'
          <dd>is an attribute specifying that all but the first argument
          of a function should be left unevaluated.
      </dl>
  
      >> Attributes[If]
       = {HoldRest, Protected}
      """
  
  
  class HoldAll(Predefined):
      """
      <dl>
      <dt>'HoldAll'
          <dd>is an attribute specifying that all arguments of a
          function should be left unevaluated.
      </dl>
  
      >> Attributes[Function]
       = {HoldAll, Protected}
      """
  
  
  class HoldAllComplete(Predefined):
      """
      <dl>
      <dt>'HoldAllComplete'
          <dd>is an attribute that includes the effects of 'HoldAll' and
          'SequenceHold', and also protects the function from being
          affected by the upvalues of any arguments.
      </dl>
  
      'HoldAllComplete' even prevents upvalues from being used, and
      includes 'SequenceHold'.
      >> SetAttributes[f, HoldAllComplete]
      >> f[a] ^= 3;
      >> f[a]
       = f[a]
      >> f[Sequence[a, b]]
       = f[Sequence[a, b]]
      """
  
  
  class NHoldAll(Predefined):
      """
      <dl>
      <dt>'NHoldAll'
          <dd>is an attribute that protects all arguments of a
          function from numeric evaluation.
      </dl>
  
      >> N[f[2, 3]]
       = f[2., 3.]
      >> SetAttributes[f, NHoldAll]
      >> N[f[2, 3]]
       = f[2, 3]
      """
  
  
  class NHoldFirst(Predefined):
      """
      <dl>
      <dt>'NHoldFirst'
          <dd>is an attribute that protects the first argument of a
          function from numeric evaluation.
      </dl>
      """
  
  
  class NHoldRest(Predefined):
      """
      <dl>
      <dt>'NHoldRest'
          <dd>is an attribute that protects all but the first argument
          of a function from numeric evaluation.
      </dl>
      """
  
  
  class Listable(Predefined):
      """
      <dl>
      <dt>'Listable'
          <dd>is an attribute specifying that a function should be
          automatically applied to each element of a list.
      </dl>
  
      >> SetAttributes[f, Listable]
      >> f[{1, 2, 3}, {4, 5, 6}]
       = {f[1, 4], f[2, 5], f[3, 6]}
      >> f[{1, 2, 3}, 4]
       = {f[1, 4], f[2, 4], f[3, 4]}
      >> {{1, 2}, {3, 4}} + {5, 6}
       = {{6, 7}, {9, 10}}
      """
  
  
  class Constant(Predefined):
      """
      <dl>
      <dt>'Constant'
          <dd>is an attribute that indicates that a symbol is a constant.
      </dl>
  
      Mathematical constants like 'E' have attribute 'Constant':
      >> Attributes[E]
       = {Constant, Protected, ReadProtected}
  
      Constant symbols cannot be used as variables in 'Solve' and
      related functions:
      >> Solve[x + E == 0, E]
       : E is not a valid variable.
       = Solve[E + x == 0, E]
      """
  