Trailing-Edge - PDP-10 Archives - decuslib20-01 - decus/20-0004/5lisp.tty
There are no other files named 5lisp.tty in the archive.

                               SECTION 5


5.1 Primitive Functions

car[x]                  car gives the first element of a list x,  or the
                        left element of a dotted pair x.  car of  NIL is
                        always  NIL.   For  all  other  nonlists,  e.g.,
                        atoms, strings,  arrays, and numbers,  the value
                        is  undefined (and  on some  implementations may
                        generate an error).

cdr[x]                  cdr gives the rest of a list (all but  the first
                        element).  This  is also the  right member  of a
                        dotted  pair.  cdr  of NIL  is always  NIL.  The
                        value of cdr is undefined for other nonlists.

caar[x] = car[car[x]]   All 30 combinations  of nested cars and  cdrs up
                        to 4 deep
cadr[x] = car[cdr[x]]   are included in the system.  All are compiled
cddddr[x] =             open by the compiler.

cons[x;y]               cons constructs a dotted pair of x and y.   If y
                        is a list, x  becomes the first element  of that
                        list.  To  minimize drum accesses  the following
                        algorithm is used in INTERLISP-10, for finding a
                        page on which  to put the  constructed INTERLISP

cons[x;y] is placed
    1)   on the page with y if y is a list and there is room;
    2)   on the page with x if x is a list and there is room;
    3)   on the same page as the last cons if there is room;
    4)   on any page with  a specified minimum of storage,  presently 16
         LISP words.

conscount[]             value  is  the  number  of  conses   since  this
                        INTERLISP was started up.

rplacd[x;y]             Places  the pointer  y in  the  decrement, i.e.,


                        cdr,  of  the cell  pointed  to by  x.   Thus it
                        physically changes  the internal  list structure
                        of x,  as opposed  to cons  which creates  a new
                        list element.   The only way  to get  a circular
                        list is  by using rplacd  to place a  pointer to
                        the beginning of a list in a spot at the  end of
                        the list.

                        The value of rplacd is x.  An attempt  to rplacd
                        NIL will cause  an error, ATTEMPT TO  RPLAC NIL,
                        (except  for  rplacd[NIL;NIL]).   An  attempt to
                        rplacd any  other non-list  will cause  an error
                        ARG NOT LIST.

rplaca[x;y]             similar  to  rplacd,  but  replaces  the address
                        pointer of x, i.e.,  car, with y.  The  value of
                        rplaca  is x.   An  attempt to  rplaca  NIL will
                        cause an  error, ATTEMPT  TO RPLAC  NIL, (except
                        for rplaca[NIL;NIL]).  An attempt to  rplaca any
                        other  non-list  will cause  an  error,  ARG NOT

Convention: Naming  a function  by prefixing  an existing  function name
with f usually indicates that the new function is a fast version  of the
old, i.e., one which has the same definition but compiles open  and runs
without any "safety" error checks.

frplacd[x;y]            Has the same  definition as rplacd  but compiles
                        open as  one instruction.   Note that  no checks
                        are made  on x, so  that a compiled  frplacd can
                        clobber  NIL,  producing  strange  and  wondrous

frplaca[x;y]            Similar to frplacd.

quote[x]                This is a  function that prevents  its arguments
                        from being  evaluated.  Its  value is  x itself,
                        e.g., (QUOTE FOO) is FOO.

kwote[x]                (LIST (QUOTE QUOTE) x),

                        if x=A, and y=B, then
                        (KWOTE (CONS x y))= (QUOTE (A . B)).

    Since    giving    quote    more    than    one    argument,   e.g.,
    (QUOTE EXPR (CONS X Y)), is almost  always a parentheses  error, and
    one that  would otherwise go  undetected, quote itself  generates an
    error in this case, PARENTHESIS ERROR.


cond[c1;c2;...;ck]      The  conditional  function  of  INTERLISP, cond,
                        takes an  indefinite number of  arguments c1,c2,
                        ... ck, called clauses. Each clause ci is a list
                        (e1i ... eni)  of n >  1 items, where  the first
                        element is  the predicate, and  the rest  of the
                        elements the consequents.  The operation of cond
                        can  be  paraphrased  as IF e11 THEN e21 ... en1
                        ELSEIF e12 THEN e22  ... en2 ELSEIF e13  ...

                        The  clauses  are  considered  in   sequence  as
                        follows: the first expression e1i of  the clause
                        ci is evaluated  and its value is  classified as
                        false (equal to NIL) or true (not equal to NIL).
                        If the value of e1i is true, the expressions e2i
                        ... eni that follow  in clause ci  are evaluated
                        in sequence, and the value of the conditional is
                        the  value of  eni, the  last expression  in the
                        clause.  In particular,  if n=1, i.e.,  if there
                        is  only one  expression in  the clause  ci, the
                        value of  the conditional is  the value  of e1i.
                        (which is evaluated only once).

                        If e1i is false, then the remainder of clause ci
                        is  ignored,  and   the  next  clause   ci+1  is
                        considered.  If no  e1i is true for  any clause,
                        the value of the conditional expression is NIL.

                        selects a form or sequence of forms based on the
                        value of  its first  argument x.   Each yi  is a
                        list of the  form (si e1i e2i ... eki)  where si
                        is the selection key.  The operation  of selectq
                        can be paraphrased as:
                        IF x=s1 THEN e1i ... eki
                        ELSEIF x=s2 THEN ... ELSE z.

                        If si is  an atom, the value  of x is  tested to
                        see if it is  eq to si (not evaluated).   If so,
                        the  expressions  e1i ... eki  are  evaluated in
                        sequence, and  the value of  the selectq  is the
                        value   of   the   last   expression  evaluated,
                        i.e., eki.

                        If si is a list, the value of x is compared with
                        each element (not evaluated) of si, and if  x is
                        eq  to any  one  of them,  then e1i  to  eki are
                        evaluated in turn as above.

                        If yi  is not  selected in one  of the  two ways
                        described, yi+1 is  tested, etc., until  all the
                        y's have been tested.  If none is  selected, the
                        value of the selectq is the value of z.   z must
                        be present.

                        An example of the form of a selectq is:


                             [SELECTQ (CAR X)
                                      (Q (PRINT FOO)
                                         (FIE X))
                                      ((A E I O U)
                                        (VOWEL X))
                                        ((NULL X)
                                        (T (QUOTE STOP]

                        which  has two  cases, Q  and (A E I O U)  and a
                        default condition which is a cond.

                        selectq  compiles  open, and  is  therefore very
                        fast; however, it will not work if the  value of
                        x is a list, a large integer, or  floating point
                        number,   since   selectq   uses   eq   for  all

prog1[x1;x2;...;xn]     evaluates its arguments in order, that is, first
                        x1, then x2, etc,  and returns the value  of its
                        first  argument  x1,  e.g., (PROG1 X (SETQ X Y))
                        sets x to y, and returns x's original value.

progn[x1i;x2i;...;xn]   progn evaluates each of its arguments  in order,
                        and returns  the value of  its last  argument as
                        its value.  progn  is used to specify  more than
                        one  computation  where the  syntax  allows only
                        one,   e.g.,   (SELECTQ ... (PROGN ...))  allows
                        evaluation of several expressions as the default
                        condition for a selectq.

prog[args;e1;e2;...;en] This  function  allows  the  user  to  write  an
                        ALGOL-like    program    containing    INTERLISP
                        expressions (forms)  to be executed.   The first
                        argument,  args, is  a list  of  local variables
                        (must be  NIL if no  variables are  used).  Each
                        atom in args is  treated as the name of  a local
                        variable  and  bound  to  NIL.   args  can  also
                        contain lists of the form (atom form).   In this
                        case, atom  is the name  of the variable  and is
                        bound  to  the value  of  form.   The evaluation
                        takes  place  before  any  of  the  bindings are
                        performed,  e.g.,  (PROG ((X Y) (Y X)) ...) will
                        bind x to the value of y and y to the (original)
                        value of x.

                        The rest of the prog is a sequence of non-atomic
                        statements  (forms) and  atomic symbols  used as
                        labels   for  go.    The  forms   are  evaluated
                        sequentially; the labels serve only  as markers.
                        The two  special functions  go and  return alter
                        this flow  of control  as described  below.  The
                        value of  the prog is  usually specified  by the


                        function  return.   If  no  return  is executed,
                        i.e., if the prog "falls off the end," the value
                        of the prog is NIL.

go[x]                   go is the function used to cause a transfer in a
                        prog.  (GO L) will cause the program to continue
                        at the label L.  A  go can be used at  any level
                        in a prog.  If  the label is not found,  go will
                        search  higher progs  within the  same function,
                        e.g.,  (PROG -- A -- (PROG -- (GO A))).   If the
                        label is not found in the function in  which the
                        prog appears,  an error is  generated, UNDEFINED
                        OR ILLEGAL GO.

return[x]               A return  is the  normal exit  for a  prog.  Its
                        argument is  evaluated and is  the value  of the
                        prog in which it appears.

If a go or return is executed in an interpreted function which is  not a
prog, the  go or return  will be executed  in the last  interpreted prog
entered if any, otherwise cause an error.

go or return  inside of a  compiled function that is  not a prog  is not
allowed, and will cause an error at compile time.

As a corollary,  go or return in  a functional argument, e.g.,  to sort,
will not work  compiled.  Also, since  nlsetq's and ersetq's  compile as
separate functions, a go or  return cannot be used inside of  a compiled
nlsetq or ersetq if the corresponding prog is outside, i.e.,  above, the
nlsetq or ersetq.

set[x;y]                This function sets x to y.  Its value is  y.  If
                        x  is  not  a  literal  atom,  causes  an error,
                        ARG NOT LITATOM. If x  is NIL, causes  an error,
                        ATTEMPT TO SET NIL.  Note that  set is  a normal
                        lambda-spread function, i.e., its  arguments are
                        evaluated  before it  is called.   Thus,  if the
                        value of x is c,  and the value of y is  b, then
                        set[x;y] would result in c having value b, and b
                        being returned as the value of set.

setq[x;y]               An nlambda version of set: the first argument is


                        not evaluated, the second is.  Thus if the value
                        of X is  C and the value  of Y is  B, (SETQ X Y)
                        would result in X (not C) being set to B,  and B
                        being returned.  If x is not a literal  atom, an
                        error  is generated,  ARG NOT  LITATOM. If  x is
                        NIL, the error ATTEMPT TO SET NIL is generated. 

setqq[x;y]              Like  setq  except  that  neither   argument  is
                        evaluated, e.g., (SETQQ X (A B C))  sets   x  to
                        (A B C).

gettopval[atm]          returns top  level value of  atm from  its value
                        cell  (even   if  NOBIND),  regardless   of  any
                        intervening bindings.  Interpreted, generates an
                        error, ARG NOT LITATOM, if atm is not  a literal
                        atom.  Compiles open without any error checks.

settopval[atm;val]      Sets top level  value of atm, regardless  of any
                        intervening bindings, i.e., stores val  in value
                        cell  of  atm.   Value  is   val.   Interpreted,
                        generates  an  error  ATTEMPT  TO  SET  NIL,  if
                        atm=NIL, or  ARG NOT  LITATOM, if  atm is  not a
                        literal atom.   Compiles open without  any error

rpaq[x;y]               An  nlambda  function like  setq,  except always
                        works  on top  level value  of x,  i.e.,  on the
                        value cell.

rpaqq[x;y]              An  nlambda function  like setqq  for  top level

rpaq and rpaqq are used by prettydef (Section 14).  Both rpaq  and rpaqq
generate errors if  x is not  a literal atom.  Both are affected  by the
value of dfnflg (Section 8).  If dfnflg = ALLPROP (and the value of x is
other than  NOBIND), instead  of setting x,  the corresponding  value is
stored on  the property list  of x under  the property VALUE.   Both are

    Since setq is an  nlambda, neither argument is evaluated  during the
    calling  process.  However, setq  itself  calls eval  on  its second
    argument.  Note  that  as  a  result,  typing   (SETQ var form)  and
    SETQ(var form)  to lispx  is equivalent:  in both  cases var  is not
    evaluated, and form is.


Changing and Restoring System State

In INTERLISP, a computation can be interrupted/aborted at any  point due
to an error, or more forcefully, because a control-D was  typed, causing
return to the top  level.  This situation creates problems  for programs
that  need to  perform a  computation with  the system  in  a "different
state", e.g., different radix,  input file, readtable, etc. or  simply a
different value for some global variable, e.g., helpflag,  dfnflg, etc.,
but want to "protect" the calling environment, i.e., be able  to restore
the  state when  the  computation has  completed.  While  errors  can be
"caught" by errorsets, control-D cannot.  Thus the system may be left in
its changed  state as a  result of the  computation being  aborted.  The
following functions address this problem:

 resetlst[resetx]       nlambda, nospread.  resetx  is a list  of forms.
                        resetlst sets up  an errorset so that  any reset
                        operations  performed by  resetsave  (see below)
                        are restored when  the evaluation of  resetx has
                        been  completed  (or  an  error  occurs,   or  a
                        control-D is  typed).  If  no error  occurs, the
                        value of resetlst is the value of the  last form
                        on resetx, otherwise resetlst generates an error
                        (after  performing the  necessary restorations).
                        resetlst compiles open.

resetsave[resetx]       nlambda,  nospread  function  for  use  under  a
                        resetlst.  If  car of  resetx is  atomic, resets
                        the  top level  value of  car of  resetx  to the
                        value     of    cadr     of     resetx,    e.g.,
                        (RESETSAVE LISPXHISTORY EDITHISTORY)  resets the
                        value  of  lispxhistory  to  be  edithistory and
                        provides for the original value  of lispxhistory
                        to  be  restored  when  the  resetlst  completes
                        operation, (or an  error occurs, or  a control-D
                        is typed).

                        If car  of resetx  is not atomic,  it is  a form
                        that is evaluated.  If cdr of resetsave  is NIL,
                        e.g.,  (RESETSAVE  (RADIX  8)),  the  form  must
                        return as its value its "former state",  so that

    i.e.,  not  conveniently.   The  program  could  of  course redefine
    control-D as a  userinterrupt, check for  it, reenable it,  and call
    reset or something similar.

    resetsave can be  called when not under  a resetlst.  In  this case,
    the restoration will be performed at the next RESET, i.e., control-D
    or  control-C  reenter.   In other  words,  there  is  an "implicit"
    resetlst at the top level in evalqt.


                        the  effect  of  evaluating  the  form   can  be
                        reversed, and the system state can  be restored,
                        by applying car of the form to the value  of the
                        form,  e.g.,  (RESETSAVE  (RADIX   8))  performs
                        (RADIX 8), and provides for radix to be reset to
                        its original  value when the  resetlst completes
                        by  applying  radix  to  the  value  returned by
                        (RADIX 8).

                        For   functions  which   do  not   return  their
                        "previous setting", the restoring expression can
                        be specified as the value of the second argument
                        to resetsave,  which in  this case  is evaluated
                        before the first argument, e.g.,
                        will  restore the  break characters  by applying
                        setbrk to the value returned by  (GETBRK), which
                        was computed  before the (SETBRK  --) expression
                        was evaluated.

                        (RESETSAVE NIL form) is permissible.   It simply
                        specifies that the value of form be treated as a
                        restoration expression, e.g.,
                        (RESETSAVE NIL (LIST (QUOTE CLOSEF) FILE))  will
                        cause file to  be closed when the  resetlst that
                        the resetsave  is under  completes (or  an error
                        occurs or a control-D is typed).

                        resetsave  compiles open.   Its value  is  not a
                        "useful" quantity.

                        Nlambda function.   Simplified form  of resetlst
                        and resetsave for resetting and restoring global
                        variables.              Equivalent            to
                        (RESETLST (RESETSAVE var new-value) form), e.g.,
                        (RESETVAR LISPXHISTORY EDITHISTORY (FOO)) resets
                        lispxhistory to  the value of  edithistory while
                        evaluating (FOO).   resetvar compiles  open.  If
                        no error occurs, its value is the value of form.

resetform[form1;form2]  Nlambda function.   Simplified form  of resetlst
                        and resetsave for resetting a system  state when
                        the corresponding function returns as  its value
                        the    "previous    setting."    Equivalent   to
                        (RESETLST (RESETSAVE form1) form2),        e.g.,
                        (RESETFROM (RADIX 8) (FOO)).  resetform compiles
                        open.   If no  error  occurs, its  value  is the
                        value returned by form2.

    Note  that  the  restoration  expression  is  still  "evaluated"  by
    applying its car to its cdr.


For  some  applications,  the restoration  operation  must  be different
depending  on  whether  the computation  completed  successfully  or was
aborted  by  an  error  or control-D.   To  facilitate  this,  while the
restoration operation is being  performed, the value of  resetstate will
be bound  to NIL,  ERROR, or RESET,  depending on  whether the  exit was
normal, due to an error, or reset (i.e., control-D, or  in INTERLISP-10,
control-C followed by reenter).  For example,

               . forms)

will cause  X to be  closed and  deleted only if  a control-D  was typed
during the execution of forms.

For  convenience in  specifying complicated  restoring  expressions, the
variable oldvalue is bound to  the value of the saving  expression.  For

                  . forms)

will restore the primary input file if an error or control-D occurs.

In addition,  the function resetundo,  in conjunction with  resetlst and
resetsave, provides a way of  specifying that the system be  restored to
its  prior  state  by  undoing  the  side  effects  of  the computations
performed under  the resetlst.  Undoing  and resetundo are  described in
Section 22.

5.2 Predicates and Logical Connectives

atom[x]                 is T if x is an atom; NIL otherwise.

litatom[x]              is T if x is  a literal atom, i.e., an  atom and
                        not a number, NIL otherwise.

numberp[x]              is x if x is a number, NIL otherwise.

Convention: Functions that end  in p are usually predicates,  i.e., they
test for some condition.

    Without using oldvalue, the user would have to write
             (SETQ TEM (INPUT FL))


stringp[x]              is x if x is a string, NIL otherwise.

arrayp[x]               is x if x is an array, NIL otherwise.

listp[x]                is x if x is a list-structure, i.e., one created
                        by one or more conses; NIL otherwise.

Note that  arrays and  strings are not  atoms, but  are also  not lists,
i.e., both  atom and  listp will  return NIL  when given  an array  or a

nlistp[x]               not[listp[x]]

eq[x;y]                 The value of eq is T, if x and y are pointers to
                        the same structure in memory, and NIL otherwise.
                        eq is compiled open by the compiler.   Its value
                        is not guaranteed T for equal numbers  which are
                        not small integers. See eqp.

neq[x;y]                The value of neq is T, if x is not eq to  y, and
                        NIL otherwise.

null[x]                 eq[x;NIL]

not[x]                  same as null, that is eq[x;NIL].

eqp[x;y]                The value of eqp is  T if x and y are  eq, i.e.,
                        pointers to the same structure in memory,  or if
                        x and y are numbers and are equal in value.  Its
                        value is NIL otherwise.

equal[x;y]              The value of equal is  T (1) if x and y  are eq,
                        i.e., pointers to the same structure  in memory;
                        or (2) eqp,  i.e., numbers with equal  value; or
                        (3) strequal, i.e., strings containing  the same
                        sequence of characters; or (4) lists and  car of

    For other string functions, see Section 10.

    For more discussion of  eqp and other number functions,  see Section


                        x is equal to car of y, and cdr of x is equal to
                        cdr of y.  The value of equal is  NIL otherwise.
                        Note that x and y do not have to be eq.

equaln[x;y;depth]       like  equal, except  if depth  of  car recursion
                        plus  depth  of  cdr  recursion  exceeds  depth,
                        assumes  equality  and does  not  search further
                        along         that          chain,         e.g.,
                        equaln[(((A)) B);(((Z)) B);2]=T.   For  use with
                        (possibly) circular structures.

and[x1;x2;...;xn]       Takes   an   indefinite   number   of  arguments
                        (including  0).  If  all of  its  arguments have
                        non-null value,  its value is  the value  of its
                        last    argument,    otherwise    NIL.     e.g.,
                        and[x;member[x;y]] will have as its value either
                        NIL or a tail of y.  and[]=T.   Evaluation stops
                        at the first argument whose value is NIL.

or[x1;x2;...;xn]        Takes   an   indefinite   number   of  arguments
                        (including 0).  Its  value is that of  the first
                        argument whose value  is not NIL,  otherwise NIL
                        if   all  arguments   have  value   NIL.   e.g.,
                        or[x;numberp[y]]  has its  value x,  y,  or NIL.
                        or[]=NIL.    Evaluation  stops   at   the  first
                        argument whose value is not NIL.

                        Is T  if the result of applying everyfn1 to each
                        element in everyx is true, otherwise NIL.
                        e.g., every[(X Y Z); ATOM]=T.

                        every        operates        by        computing
                        everyfn1[car[everyx]].    If  this  yields  NIL,
                        every immediately returns NIL.  Otherwise, every
                        computes  everyfn2[everyx],  or  cdr[everyx]  if
                        everyfn2=NIL, and uses this as the "new" everyx,
                        and     the     process     continues,     e.g.,
                        every[x;ATOM;CDDR]  is   true  if   every  other
                        element of x is atomic.

                        every compiles open.

    A loose description of equal might be to say that x and y  are equal
    if they print out the same way.

    Actually, everyfn1[car[everyx];everyx]  is computed, so  for example
    everyfn1 can look at the next element on everyx if necessary.


                        value is  the tail of  somex beginning  with the
                        first element that satisfies somefn1,  i.e., for
                        which somefn1 applied  to that element  is true.
                        Value  is  NIL  if  no  such  element  exists.  
                        e.g., some[x;(LAMBDA (Z) (EQUAL Z Y))]        is
                        equivalent   to   member[y;x].    some  operates
                        analogously     to     every.     At each stage,
                        somefn1[car[somex];somex]  is  computed,  and if
                        this is not NIL, somex is returned as  the value
                        of some.  Otherwise, somefn2[somex] is computed,
                        or cdr[somex] if  somefn2=NIL, and used  for the
                        next somex.

                        some compiles open.

                        same as not[some[somex;somefn1;somefn2]]


memb[x;y]               Determines if x is a member of the list y, i.e.,
                        if there is an element of y eg to x.  If so, its
                        value is  the tail of  the list y  starting with
                        that element. If not, its value is NIL.

fmemb[x;y]              Fast  version of  memb that  compiles open  as a
                        five  instruction  loop, terminating  on  a NULL
                        check.   Interpreted,  fmemb  gives   an  error,
                        BAD ARGUMENT - FMEMB,  if y  ends in  a non-list
                        other than NIL.

member[x;y]             Identical  to  memb except  that  it  uses equal
                        instead of eq to check membership of x in y.

The reason for the existence of both memb and member is that eq compiles
as one instruction but equal requires a function call, and  is therefore
considerably more expensive.   Wherever possible, the user  should write
(and use) functions that use eq instead of equal.

tailp[x;y]              Is x, if x is a tail of y, i.e., x is eq to some
                        number  of cdrs  > 0    1 of  y, we  say x  is a
                        proper tail.> of y, NIL otherwise.

    If x is eq to some number of cdrs


assoc[key;alst]         alst is a list of lists (usually  dotted pairs).
                        The value of assoc is the first sublist  of alst
                        whose car is eq to  key.  If such a list  is not
                        found, the value is NIL.  Example:
                        assoc[B;((A . 1) (B . 2) (C . 3))] = (B . 2).

fassoc[key;alst]        Fast version of assoc that compiles open as  a 6
                        instruction loop,  terminating on a  NULL check.
                        Interpreted, fassoc gives an error if  alst ends
                        in  a non-list  other than  NIL, BAD  ARGUMENT -

sassoc[key;alst]        Same as assoc but uses equal instead of eq.

putassoc[key;val;alst]  Searches alst for an element car of which  is eq
                        to key.  If one is found, cdr is replaced (using
                        rplacd) with val.  If no such element  is found,
                        cons[prop;val]  is  added at  the  end  of alst.
                        Value is ?.  If alst is NIL, generates an error,
                        ATTEMPT TO RPLAC NIL.  Otherwise, if alst is not
                        a list, generates an error, ARG NOT LIST.

listget[lst;prop]       Similar  to  getprop (Section  7)  but  works on
                        lists using property list format.   Searches lst
                        two elements at  a time, i.e., by  cddr, looking
                        for an  element eq  to prop.   If one  is found,
                        returns the next element of lst,  otherwise NIL.
                        Generates an ARG NOT LIST  error if lst  is non-
                        NIL and not a list.

listput[lst;prop;val]   Similar  to  putprop.   Searches  lst   by  cddr
                        looking for  prop.  If  prop is  found, replaces
                        the next  element of  lst with  val.  Otherwise,
                        prop and val are added to the end of lst.  Value
                        is ?.  Generates an ARG NOT LIST error if lst is
                        not a list.

listget1[lst;prop]      Like listget but searches lst one cdr at a time,
                        i.e., looks at each element.

listput1[lst;prop;val]  Like listput  except searches lst  one cdr  at a