December 11, 2019

Melting, everywhere

Melting of large deposits of ice, in glaciers and in permafrost, has been frequently with about in the recent years. When it comes to meeting on Greenland, we seem to be following the rates of the worst case predictions of older IPCC reports.

https://www.washingtonpost.com/climate-environment/2019/12/10/greenland-ice-losses-have-septupled-are-pace-sea-level-worst-case-scenario-scientists-say/

https://www.tiede.fi/artikkeli/uutiset/gronlanti-sulaa-kuumimman-ennusteen-mukaan

There are some alarming developments in the Arctic permafrost of Sundries and Alaska. It seems like we will soon be crossing one threshold of positive feedback very soon.

https://www.washingtonpost.com/weather/2019/12/10/arctic-may-have-crossed-key-threshold-emitting-billions-tons-carbon-into-air-long-dreaded-climate-feedback/


“Especially noteworthy is the report’s conclusion that the Arctic already may have become a net emitter of planet-warming carbon emissions due to thawing permafrost, which would only accelerate global warming. Permafrost is the carbon-rich frozen soil that covers 24 percent of the Northern Hemisphere’s land mass, encompassing vast stretches of territory across Alaska, Canada, Siberia and Greenland. 
There has been concern throughout the scientific community that the approximately 1,460 billion to 1,600 billion metric tons of organic carbon stored in frozen Arctic soils, almost twice the amount of greenhouse gases as what is contained in the atmosphere, could be released as the permafrost melts.”
https://www.nytimes.com/2019/12/14/opinion/sunday/climate-change-arctic.html
The ice is a great white shield that reflects incoming solar warming back to space during the long summer days of the midnight sun. Otherwise, it would be absorbed by the ocean. Losing this ice, the study explained, would be the warming equivalent of an extra 25 years of emissions at current rates.”

Long time no see

I haven't written anything in quite a while. Maybe I should.

This  page has been functioning as my dumping found for excess thoughts, caused by reading way too much news, especially during the years 2002-2009 when I had something like an addiction, following the war on Iraq and the global economic downturn, fueled by the subprime crisis. I have certainly followed news very actively, but not with such an addiction anymore.

Recently, there has been a heightened level of interest in physical signs of climate change, rise of far right populist movements and overall political polarization in developed countries. I feel that a prolonged global trade war between the US and China is still just in its infancy, and will get quite a lot more intense, before settling into a new status quo.

The next economic downturn in the US will leave much more devastation behind it than most people currently realize, due to the pro-cyclic Republican tax policies that have produced a massive budget deficit at the very top of the business cycle. There will be no room for stimulus in the next downturn. The result will be either a self-enforcing austerity or a dramatic collapse in the value of the dollar and possible loss of US credit rating.

After some strange, partially media-led turmoil, our Finnish government is now led by a much younger group of people than ever since the 70's. This is a welcome development, but I hope they keep their feet on the ground. The centre-leftist government is met with a corporate media dead-set against some of its goals. It is difficult to filter the substance from the opinion.

With these developments, I am feeling that there is a risk of getting a information indigestion. It is certainly not helped by all the information that I need to digest in my professional life, which nowadays seems to consist of continuous learning of new software development tools and libraries.

December 20, 2017

Haskell and type composition the easy way

Below is a small Haskell module that I wrote as an exercise. It defines nifty little utility in the form of a generic type composition mechanism. A composite type is any type in which a type constructor is applied to a another type (denoted with type variables, like a (b c)). It can represent for example a list of optional values, i.e [Maybe Int], aka [] (Maybe Int), or an IO operation sequence that operates on a list of values (IO [a]), etc.

Haskell provides many useful high-level programming constructs for managing individual types, but its standard libraries lack a truly flexible denotation for extending those high-level functions to composition data types. Some of these issues are solved by using so-called "monad transformers", which are very sophisticated, but must be separately defined for each individual monad. And as the name says, their application is limited to monads, and they cannot be used for the many quite useful classes of type constructors at the lower levels of the functor hierarchy, such as Functors, Foldables, Traversables or Applicatives.

The small (and silly) test function at the end of the modules shows, how this modules enables flexible intermingling of overlapping data types using both the monadic do syntax and plain fmap calls. While this only seems to apply to compositions of two types at a time, it quite easily generalizes to any number of types, via meta-composition. If a, b, c ... g are functors, then so is (Comp a b). IO [Maybe Int] can be tagged as Comp (Comp IO []) Maybe Int. Hmmm... Maybe the next step is to generalize these operations to a recursive data type representing arbitrary chains of composition.

One cannot help but appreciate how all of these very useful operations are relatively short one-liners. (Readability for persons not familiar with Haskell or Scala is another question.

{-# LANGUAGE FlexibleContexts #-}

-- A generic type composition module by Reino Ruusu, December 2017, Espoo, Finland

-- The simple type composition tag defined here can do many of the tasks that
-- monad transformers are used for, but in a much more generic way, allowing
-- useful compositions of Functors, Foldables, Traversables, Applicatives and
-- Monads (with certain restrictions).

import Control.Applicative
import Control.Monad
import Data.Foldable
import Data.List

-- This type tags a composition type as a parameterized type constructor
newtype Comp m n a = Comp (m (n a))

-- Decorate a `raw' composition type
comp :: m (n a) -> Comp m n a
comp = Comp

-- Undecorate a tagged composition type
decomp :: Comp m n a -> m (n a)
decomp (Comp x) = x

-- Lift a raw outer type to the composition type (inner has to be Applicative)
lift1 :: (Functor m, Applicative n) => m a -> Comp m n a
lift1 = Comp . (pure <$>)

-- Lift a raw inner type to the composition type (outer has to be Applicative)
lift2 :: (Applicative m) => n a -> Comp m n a
lift2 = Comp . pure

-- Foldable instance
instance (Foldable m, Foldable n) => Foldable (Comp m n) where
  -- foldMap :: Monoid k => (a -> k) -> Comp m n a -> k
  foldMap f (Comp x) = foldr (\a l -> foldMap f a `mappend` l) mempty x 

-- Functor instance
instance (Functor m, Functor n) => Functor (Comp m n) where
  -- fmap :: (a -> b) -> (Comp m n a) -> (Comp m n b)
  fmap f (Comp x) = Comp (fmap (fmap f) x)

-- Applicative instance
instance (Applicative m, Applicative n) => Applicative (Comp m n) where
  -- pure :: a -> Comp m n a
  pure = comp . pure . pure
  -- (<*>) :: Comp m n (a -> b) -> Comp m n a -> Comp m n b
  Comp f <*> Comp x = Comp (liftA2 (<*>) f x)

-- Monoid instance
instance (Applicative m, Applicative n, Monoid a) => Monoid (Comp m n a) where
  -- mempty :: Comp m n a
  mempty = pure mempty
  -- mappend :: Comp m n a -> Comp m n a -> Comp m n a
  mappend = (*>)

-- Monad instance (only works if inner type is Traversable, unlike IO, for example)
instance (Monad m, Monad n, Traversable n) => Monad (Comp m n) where
  -- return = pure
  -- (>>=) :: Comp m n a -> (a -> Comp m n b) -> Comp m n b
  Comp x >>= f = Comp $ x >>= fmap join . sequence . fmap (decomp . f)


-- Test routine, builds a (Comp IO []) monad in the do block
-- First fmap (show) applies to (Comp IO [] Int), resulting in (Comp IO [] String).
-- After decomp, second fmap applies to (IO [String]).
-- Execution results in 4*(1 + 4) calls to print, and a return value of
-- "4, 5, 6, 7, 3, 4, 5, 6, 2, 3, 4, 5, 1, 2, 3, 4"

test :: () -> IO String
test () = fmap (intercalate ", ") $ decomp $ fmap (show) $ do
  a <- lift2 [1, 2, 3, 4]
  lift1 (print a)
  b <- lift2 [5, 6, 7, 8]
  lift1 (print (a, b))
  return (b - a) :: Comp IO [] Int

November 26, 2017

The beauty of Haskell

I've been taking a more serious look into Haskell programming, and have deeply fell in love with its "purity", i.e. its nature as a purely functional programming language, in which everything is expressed entirely in terms of what happens to data.

Another very attracting feature is its very rich toolbox of higher level programming constructs that allow one to express complex data processing tasks in just a few function calls.

A third beautiful aspect of the language is its typing system, which provides automated type matching of undeclared functions and variables. It provides a type checking system just like any other strongly typed language, but without the need to separately declare the types of each and every variable.

On the other hand, these features make Haskell quite difficult to read for people who are not familiar with it, but on the other hand, the amount of code to read can be very small, and absolutely oozing with semantics, i.e. there code is typically very dense, and the reader is not bothered with unnecessary details, such as the name of an iteration variable.

The way in which stateful programming is expressed in Haskell, which is a pure language in the sense that absolutely no side effects are allowed in the code, can be a bit confusing for a beginner, but after one gets the hang of it, one starts to really appreciate the pure functional programming paradigm.

With no side effects and a strong typing system, any program that compiles actually does something, there is no concept of a run-time failure, except in the case of a match against a partially defined pattern, or similar situations.

Additionally, lazy evaluation allows one to express interactive processes simply as ordinary pure functions, which is really convenient, though may be quite confusing.

Below is an example of a hangman program, written in Haskell, using lazy evaluation and the State monad. The program lets the user guess a word one letter at a time. If the user makes 5 wrong guesses, he loses the game.


-- A hangman game using the State monad in Haskell by Reino Ruusu, 2017

import System.Environment
import System.Random
import Control.Monad.State.Lazy
import Data.Char

-- Hangman IO
main = do
  args <- getArgs
  if null args then
    putStrLn "Please provide a file containing words. (One per line.)"
  else do
    -- Select random word
    words <- readFile (head args)
    word <- randomElement (lines words)
    -- Play the game
    interact $ hangmanMain (map toLower word)

-- Hangman game as a pure lazy string processing function
hangmanMain :: String -> String -> String
hangmanMain word = unlines . ("Welcome to Haskell Hangman":) . hangman . ("":) . lines
  where
    hangman input = map snd $ takeUntil fst $ evalState (sequence steps) initialState
      where
        steps = map (hangmanIteration word) input
        initialState = (initialGuess, 0)
          where
            -- Alphabetic characters replaced with underscores
            initialGuess = map (\w -> if isAlpha w then '_' else w) word

-- A single iteration of hangman: State update followed by output
hangmanIteration :: String -> String -> State (String, Int) (Bool, String)
hangmanIteration word input = update >> report
  where
    update = unless (null input) $ do
      -- Check user's guess and update state
      (guess, strikes) <- get
      if elem l word && not (elem l guess) then
        put (newguess guess, strikes) -- Hit
      else
        put (guess, strikes + 1) -- Miss
      where
        l = head input
        newguess = zipWith (\w g -> if w == l then w else g) word
    report = do
      (guess, strikes) <- get
      -- Status message - example: __a_e__ ###
      let status = guess ++ " " ++ (replicate strikes '#')
      -- Check game outcome
      return $ if guess == word then
                 (True, status ++ "\nYou won!") -- Win
               else if strikes >= 5 then
                 (True, status ++ "\nYou lost! (" ++ word ++ ")") -- Loss
               else
                 (False, status) -- Continue

-- Utility functions

-- Take elements up to and including the first for which f returs True
takeUntil f l = first ++ [head last]
  where (first, last) = break f l

-- Select a random element from a list (as an IO operation)
randomElement list = do
  i <- randomRIO (0, length list - 1)
  return (list !! i)



The execution of the game looks like this:
Welcome to Haskell Hangman
____________
a
____________ #
e
____________ ##
i
_i________i_ ##
o
_i__o_____i_ ##
u
_i__ou____i_ ##
s
_is_ou__s_i_ ##
c
_iscou__s_i_ ##
v
viscou__s_i_ ##
t
viscou_ts_i_ ##
y
viscou_ts_i_ ###
n
viscounts_i_ ###
p
viscounts_ip ###
l
viscounts_ip ####
h
viscountship ####
You won!
Note how the game itself is defined as a pure function that simply processes the input string into the output string. Furthermore, all iteration in the code happens via higher level programming constructs that clearly define rules for processing of data, instead of bothering with low-level things such as updating an iteration variable. All the IO is performed by calling this function via the interact function, which simply maps input and output from the console to a pure function from string to string. The business logic can then be defined purely by describing the relationship between the input and the output, without bothering with any aspects of the IO operations.

The steps of the game are here described using the State monad, which allows one to define processing as a combination of stateful operations.

An almost equally elegant solution can be achieved by the very versatile mapAccumL operation, which provides for simultaneous accumulation of state and processing of data. However, in this approach, the hangmanIteration function is much less understandable in isolation, whereas in the version based on the State monad, the state update is directly specified by the definition of the iteration function itself. The state monad allows us to also make use of elegant and semantically rich higher level constructs, such as unless above.


-- A hangman game in Haskell by Reino Ruusu, 2017

import System.Environment
import System.Random
import Data.List
import Data.Char

-- Hangman IO
main = do
  args <- getArgs
  if null args then
    putStrLn "Please provide a file containing words. (One per line.)"
  else do
    -- Select random word
    words <- readFile (head args)
    word <- randomElement (lines words)
    -- Play the game
    interact $ hangmanMain (map toLower word)

-- Hangman game as a pure lazy string processing function
hangmanMain :: String -> String -> String
hangmanMain word = unlines . ("Welcome to Haskell Hangman":) . hangman . ("":) . lines
  where
    hangman input = map snd $ takeUntil fst output
      where
        output = snd $ mapAccumL (hangmanIteration word) initialState input
        initialState = (initialGuess, 0)
          where
            -- Alphabetic characters replaced with underscores
            initialGuess = map (\w -> if isAlpha w then '_' else w) word

-- A single iteration of hangman: State update followed by output
hangmanIteration :: String -> (String, Int) -> String -> ((String, Int), (Bool, String))
hangmanIteration word state input = (newState, report newState)
  where
    -- Check user's guess and update state
    newState = if null input then
                 state
               else if elem l word && not (elem l guess) then
                 (newguess guess, strikes) -- Hit
               else
                 (guess, strikes + 1) -- Miss
      where
        l = head input
        (guess, strikes) = state
        newguess = zipWith (\w g -> if w == l then w else g) word
    report (guess, strikes) = let
      -- Status message - example: __a_e__ ###
      status = guess ++ " " ++ (replicate strikes '#')
      -- Check game outcome
      in if guess == word then
           (True, status ++ "\nYou won!") -- Win
         else if strikes >= 5 then
           (True, status ++ "\nYou lost! (" ++ word ++ ")") -- Loss
         else
           (False, status) -- Continue

-- Utility functions

-- Take elements up to and including the first for which f returs True
takeUntil f l = first ++ [head last]
  where (first, last) = break f l

-- Select a random element from a list (as an IO operation)
randomElement list = do
  i <- randomRIO (0, length list - 1)
  return (list !! i)

January 30, 2015

Building standalone Simulink models with calls to external libraries

Matlab ​Simulink can compile and build simulation models into stadalone executables that do not require the presense of any runtime binaries from Matlab. However there are a few limitations, that I have had to overcome this week:

  1. There can be no algebraic loops in the model.
  2. Certain Matlab functions are not available.

Eliminating algebraic loops in the model is sometimes possible by reformulating the problem. This is also a good idea in any case, as the convergence of the algebraic loop solver of Simulink is not in any way perfect, possibly resulting in a termination of the simulation.

Below is presented a way to overcome the second restriction in relation to functions that are used for calling external libraries.

Matlab provides a few functions for calling functions in a dynamically loaded library file. (.dll in Windows, .so in Linux):

  • loadlibrary(libname, hfile)
    Loads a DLL file into Matlab.
    • libname is the name of the library, wihtout the file name suffix (.dll/.so)
    • hfile is the name of a C-language header file that declares the functions provided by the DLL.
  • calllib(libname,funcname,arg1,...,argN)
    Calls a function provided by the DLL.
    • loadlibrary must be used before calling this function.
  • libpointer(datatype, value)
    Creates a pointer object that can be used for reading data that is written by a library function into a buffer provided as an argument.
These functions are very convenient, because they can automatically transform the Matlab data types into the correct forms expected by the library function. However, they are not available for use in a compiled Simulink model. When executed within Matlab, the Simulink model is always compiled into an S-function. To be able to access loadlibrary, callib and libpointer, they must be declared using coder.extrinsic(), which provides access to these functions.

However, when a Simulink model is compiled into a completely standalone executable, these functions are not available even with the coder.extrinsic() declaration. If the model contains a user-defined block, such as "MATLAB function", "Level-2 MATLAB S-Function" or "MATLAB System", which contains calls to library functions using the above-mentioned functions, building a standalone executable will fail with an error message: "The extrinsic function 'libpointer' is not available for standalone code generation. It must be eliminated for stand-alone code to be generated."

However, there is an alternative way of calling external library functions from compiled Matlab code:

  • coder.ceval('FCN',X1,...XN)
    This is a function that can only be used in MATLAB code that is compiled into C code. It is a kind of macro that simply places a call into the generated C code without making any checks on its arguments.
  • coder.cinclude(headerfile)
    Causes the addition of an #include statement in the generated C code.

Unfortunately the use of calllib and coder.ceval is mutually exclusive; calllib can only be used in non-compiled models and coder.ceval can only be used in compiled models. Fortunately this can be checked at "run-time" in the Matlab code, by checking the value of the variable coder.target. When code is compiled into an S-function within Matlab, this variable has the value 'sfun'. When the code is compiled into a standalone model executable, this variable has the value 'standalone'. This can be checked in the Matlab code using ordinary if statements, so it is possible to write custom blocks that work both in Matlab and as standalone executables.

  use_coder = ~strcmp(coder.target, 'sfun');

  if use_coder
     coder.ceval('myfunction', arg1, arg2, arg3);
  else
     calllib('library', 'myfunction', arg1, arg2, arg3);
  end

Unfortunately it is not so easy as this. To be able to use coder.ceval(), the exectable must be linked against the DLL using a .lib file, forming a fixed dependency between the executable and the DLL(s). This unfortunately prevents calls to identically named functions in several different DLLs.

A bigger difference between coder.ceval() and calllib() is that coder.ceval() makes absolutely not checks or conversions for the datatypes of the arguments. All arguments must already be of a suitable data type, and any variables that are to be passed using a pointer (or reference in C++) must be explicitly declared using coder.ref(). Otherwise the result will most probably be a hard crash of the standalone model.

Lets take as an example the following C language delaration for a function that returns the average of an array of floats and writes the standard deviation into a pointer value.

  double stats(float *buf, double *sd_out, int bufsize);

While completely okay when used with callib(), the following call has a number of problems.

  n = 10;
  values = rand(1, n);
  sd = 0.0;

  // This will crash!!
  mean = coder.ceval('stats', values, sd, n);

The vector 'values' would be passed into the function as double pointer instead of float, as required. 'sd' is passed by value, not as a pointer. 'n' would be passed as a double instead of an int.
Finally, without a prior declaration of 'mean', Matlab Coder has no way to infer the data type of the return value, resulting in an error during build.

Below is a corrected version of the call:

  n = int32(10);
  values = single(rand(1, n));
  sd = 0.0;
  mean = 0.0;
  mean = coder.ceval('stats', coder.rref(values), coder.ref(sd), n);

To catch more potential typing errors, a call to coder.cinclude('libraryheader.h') should be located at some point, so that a declaration for the library function is added to the compiled C code.

It is quite natural for libraries to require calls to some kind of initialization functions, and a very natural place to locate these would be in an initialization function for the block that uses the library. Unfortunately, when building a standalone executable, these calls will be made before the Simulink Coder starts to build the executable, and the initialization calls will be completely omitted from the standalone model, resulting in possibly mysterious errors.

A possible solution is to move any such calls from the initialization functions into a conditional clause at the dependent code, which is only executed at the first call. An easy way to achieve it is to use a local persistent variable like this:

  function y = fcn(xin, par1, par2)
    persistent initialized;
    if isempty(initialized)
      initialized = true;
      coder.ceval('myinitializationcall', par1, par2);
    end
   ...
  end

To gain access to necessary parameter values for the relocated initialization call, some additional parameters may need to be added to the code block. Below is an example of how to do this using the Model Explorer.

Let's say that we have a Matlab function block like this:



The block has a mask that defines a single integer input (Parameter1) that is used as an argument in an initialization call to a custom library:



The function itself is also implemented as a simple call to the same library:

  function y = fcn(u)
    coder.extrinsic('callib');
    y = callib('mylibrary', 'myfun', u);
  end

The coder.extrisic() call is necessary here, because the function is compiled into a MEX function by Simulink, even when the model is simulated in Matlab. The initialization call on the other hand is made by the Matlab interpreter, in which calllib() is always available.

Now if we want to be able to use the block in a standalone Simulink model executable, we need to replace the initialization calls in the mask with calls that are made in the function itself. For this purpose, we can add an additional argument for the function, which provides the value of the mask parameter. For this, we need to open the Model Explorer and select the MATLAB Function block (Right-click/Explore will do). With the "Add Data" command, we can insert the necessary parameter into the function. Note, that the scope of the added data item must be set to "Parameter", instead of the default scope of "Input".



Then we can add the equivalent call to coder.ceval() into the function and also add a conditional code block for the initialization call, like this:

  function y = fcn(u, Parameter1)
    coder.extrinsic('callib');
    persistent init_called;

    if strcmp(coder.target, 'sfun')
      y = callib('mylibrary', 'myfun', u);
    else
      coder.cinclude('mylibheader.h');
      if isempty(init_called)
        init_called = true;
        param = int32(Parameter1);
        coder.ceval('myinit', param);
      end
    
      % make sure of the right types
      y = 0.0;
      tmp = double(u);
      y = coder.ceval('myfun', tmp);
    end
  end

To be able to build the model, we also need to add 'mylibrary.lib' as an external library in the code generation configuration for the whole model (Code Generation/Custom Code/Libraries). After doing this, we are ready to build the model as blazingly fast and completely standalone executable with no additional runtime library dependencies.



July 13, 2012

Drone Warfare, Blowback and PTSD

In The Real Blowback Fallacy at antiwar.com, John Poindexter has good counterarguments to Christopher Swift in foreignaffairs.com.
It means that if five members of his group agreed that drone strikes aid in recruiting AQAP members, then roughly 3,000,000 other Yemenis must also support that conclusion.
He is spot on contrasting the local actions of AQAP with those of the US.
It certainly shouldn’t be the role of the U.S. to police Yemen, but if the money is going to be spent, I would rather see it go to feeding some poor family or digging wells in the desert than burning the flesh off infants or dismembering whole families at random.
Despite all the talk about precision strikes, the fact is that since Vietnam, force protection has been allowed to completely dominate over the need to avoid collateral damage. Even "precision" use of air force will always cause much more collateral damage than would be caused by ground forces.

As it happens, drone warfare can be even more damaging to psyche of the operator than participation in ordinary warfare. Going to work to kill people during the day, perhaps just kilometers away from one's home, and returning home every evening to spend time with the family, makes the killing a normal part of everyday life, causing a far deeper impact on one's conscience than acts that are made in the special circumstances of ordinary warfare. P.W. Singer explains it well in this video.


This problem has been well recognized by the people involved, but the solution proposed by David Axe a few weeks ago is definitely not the right solution.
A more independent drone could alert its controller for assistance only when it has spotted a likely target. The operator would give a thumbs-up or thumbs-down for the robot to fire a weapon. With only minimal involvement, the human being could avoid feeling fully responsible for the consequences of the strike. Drones are already becoming more autonomous by the day, opening the door for a different emotional dynamic between them and their operators.
Besides being flagrantly immoral, this would just heavily increase the number of collateral casualties and cause even bigger blowback.

April 26, 2010

About My Master's Thesis

This post might well be the first that has any connection at all to my everyday life.

I haven't written anything here for a very long time. I have been busy enough with other things. On my spare time I have been finishing my long overdue master's thesis at the Helsinki University of Technology, which is nowadays a part of the Aalto University in Helsinki, Finland.

My thesis concerns digital texturing of solid objects, and in it I describe how texture mapping, as it is understood in computer graphics, can be used for designing objects with custom low-level surface details. The work is based on an old project in which we produced highly accurate laser-machined details into the surfaces of plastic injection molds, based on ordinary bump map images and 3D models of the mold cavities. The point of my thesis is that digital texture mapping is a viable tool for the design and manufacturing of (embossed) surface details, as long as the right tools are made available.

As one part of the work I developed a new method for adaptive displacement mapping of triangular meshes that is specifically aimed at well-specified tolerances at an optimal number of output primitives. It can be readily used to produce textured geometries for rapid prototyping or laser machining.

It's applicability is not necessarily limited to manufacturing, but could be used for visualization as well. At least it easily beats the adaptive displacement in 3dsMax.

Here is a flat shaded rendering of a sample model from the algorithm, along with the tileable displacement texture. (It's an old concept design, not any actual phone model.)




If anyone is interested in asking more, please feel free to email me at vtt.fi (firstname.lastname).