 Okay, so this talk is about Scython, who knows what Scython is? Or actually, who does not know what Scython is? Okay, that's cool, I didn't know one. That's cool. So this is kind of a general, like I'm going to show you a broader view of everything kind of talk. And so if you're already a Scython guru, you may not learn much, but you'll certainly see a lot. So to my person, I'm a Python developer, and most of the time I'm also a Python advocate since 2002, so it's been a while. And I'm currently working for Scooby, who does not know what Scooby is. Okay, yeah, a couple of people. Anyone likes reading books? Okay, go to Scooby the A, take a look. We are just great. You get unlimited reading for Android, iOS, like any device you like. And we change the way people read books. It's really that way it is. Apart from that, I'm a freelance developer, a consultant, trainer and this kind of stuff, so I actually do Scython trainings. So if you're interested in getting a bit deeper into the matter, you can talk to me after the talk. Or go to that page and have a look at what else I have. Then, well, there's a couple of other open source tools I'm working on, so I'm involved in the developer of LXML, the XML toolkit for Python. I've written a Lula integration for Python. Both of them are actually written in Scython, and so the main project that I'm working for is Scython. Scython is the topic of the talk today. So three little parts, a little intro, so we'll show you what Scython is, what it gives you, what's cool about it. Then a major part about Scython's type system, which actually involves a little demo, so you'll see Scython code, you'll learn how it works. What makes it cool, why it's so great to integrate with C code using Scython. Why it's so cool to write Scython code, how it works. And then in the end, a little quick high-performance features that we've added to the compiler. I actually got a bit confused with the times. I thought I only had half an hour. I actually have 45 minutes for this talk, and then you'll see a longer demo. So what is Scython? Scython is an open source project. You can go to Scython.org, take a look at the project, talk to us. There's a mailing list for it. Obviously, user's mailing list, so if you have any questions about it, just go to the user's mailing list. It's on, well, sadly, it's on Google groups, but you'll manage anyway. Ask your questions there, just come along. We are very happy to answer them. It's an open source project. It's a Python compiler, or almost a Python compiler, meaning you can actually take normal regular Python code and just throw it into the compiler and have it compile to C. It translates Python, actual Python source code, to C, using the C Python C API. And it features static code optimization, so you usually get a kind of a notable performance boost when you do that. The third thing that Scython is, it's an extended Python language, so you can not only throw Python code in there, you can also throw actual Scython code in there, which has an extended syntax, so it has additional constructs, which allow you to provide static type annotations, which tell the compiler, okay, make this fast, this is important, drop in to C, you'll see. And so it allows you to write fast extension modules for C Python and interface Python with external native libraries, usually in C or C++, but there's integration for 4.2, for example, and the C interface can be used to talk to any native code anyway, so just anything you'll find. Okay, so how do you use Scython? Well, basically you write Python code or Scython code. Scython then translates it into C code. The C compiler builds a shared library for C Python and you can import that module into Python. Okay, it works in Python 2 and Python 3. Actually, we had Python 3 support even before Python 3.0 was out and then we re-edit Python 3 support when it actually came out and didn't match the old implementation anymore, and we had the same for a couple of other C Python versions later, so it happens. It keeps happening actually, but we arrived in the code that you don't have to write. Okay, here's an example. So from compiling Python code, this is like a stupid little Python script. It has a class, doing nothing interesting. It has a function, passes the function into the class, executes it into, you know, the example. And then when you compile it, you can just use the Scythonize command for it. So when you install Scython, this will come with it, actually only in the next version, so the current version doesn't install yet, I think. But we're working on releasing it. There's the Scythonize script, you can just say Scythonize minus i, so do an in-place build of this thing. It compiles it, builds the shared library for you, and then you get a C file from it and the shared library, which you can just employ it and use. And it translates to 3,000 lines of C code. So that's a bit more than Python code you just saw. Why is that? Well, there's lots of quadabilities defined in there, which makes it support different C compilers, different Python versions, as I said, version two, version three, different minor versions of Python, lots of differences that came in over time. So the compiler actually has a lot of knowledge about how C Python evolves over time and adapts your code to it at compile time. There's lots of helpful C comments in the generated code that mimic your code, so it shows what code you have and that makes it kind of easier to trace your code through the generated code in case you ever have to do that. And there's definitely a lot and a lot of code that you definitely absolutely do not want to write yourself. Okay? So we write C codes so you don't have to. Okay? Okay, how do you do that? Now, in the back of this Python script, and actually, like, whenever you want to distribute a package to yourself, you'll use these details for it, normally for packaging it up and distributing it. And this is how you use Python in your distributed build. So you would just say add extension modules and they come from siphonizing some source files. Okay? And Python would just pick up the source files, compile them, build an extension object, so the metadata for it, and push it into the distributed setup function to build it all. For the more complex cases where you need an explicit configuration of your extension modules, for example, you're building against specific C libraries where you have some kind of dependencies and need to pass in options or something, you can, well, as before, as for any C extension module that you would build in Python or in this unit, you would normally create this extension object for them, which is the metadata for the build. Here, Sidenize does it for you. If it's more complex, you can do it yourself and just pass it into Sidenize. Okay? Works the same way. That just makes Sidenize pick up the metadata and build your code correctly. Okay. So what you get out of it is highly portable code. So Sidenize generates C code that compiles basically with all major C++ compilers, which means GCC normally, MSVC on Windows, but there are also people using it with the Intel compiler, for example, and it's production tested, right? Works on all major platforms, Linux, Mac, Windows, people using it on BSD, on really big machines, on all sorts of platforms, and it works in Python 2.4 through 3.4 currently, and we're keeping track of the development in 3.5, so that'll work as soon as it comes out. We dropped support, though, for really old Python versions, so the next release that we make will not support 2.4, 2.5 anymore, and, like, the old 3.1 version that no one should be using anymore anyway. But it still supports any recent, any somewhat recent Python version, even that version. So the Python language syntax itself normally follows Python 2.7, but we support Python 3 syntax if you want to write source code for Python 3. You just have to tell the compiler this is code with language level 3, this is a compiler directive, and it'll just compile your code in Python 3 mode. Regarding language features of Python itself, we compile and run more than 98% of the regression test seed that comes with CPython, which means that we have, like, pretty much complete Python language support. We support classes, functions, closures, generators, like all sorts of features that you'll see in your daily life. Anything that says Python features is supported by the compiler. Comprehensions, any control structures, all sorts of stuff. There are only a couple of minor deviations that you're rather unlikely to meet in practice, which is we don't have the frames and functions. Does anyone know what frames are? Yeah, that's a couple of people, see? You don't need that. So we only have them for exceptions and profiling, okay? Which is the cases where you may actually meet them without knowing. And there are a couple of minor bugs. I can't see the URL, but just go to our homepage and click on it. Okay, so for speed. Python generates very efficiency code. There are lots of static and optimistic optimizations that applies to your code. It generates optimized code for the standard Python types, built-in types, many built-in functions also, which means that your code will simply run faster because Python understands it. There's static type inference inside of functions, so you don't have to declare many types, you don't have to tell it, like, this is list, this is an integer, this is whatever. It'll understand your code partly automatically, and you can help it a bit to make it understand it better. Okay. So a bit more about speed. There's a Python benchmark suite, which contains real, pure Python codes, including Django, including a couple of template engines, actually a couple of computational modules, like the real Python code. And just by compiling it, you get a factor of 1.2 to 2.4. It doesn't sound all that much compared to Python 3.4, but that's what you get out of the box. And you can make it a lot faster by, you know, add a little hand-tune. How's that done? Well, static type declarations. So, Python allows you to put kind of type hints into your code, which tells the compiler that, you know, this is not just any arbitrary Python object, it's actually enough to represent as a C int, and that will make the compiler drop some parts of the code that used this variable into plain C, instead of doing object operations on it. Some languages call this unboxing, so you may know it from Java, for example. You just, you know, the idea is to drop object operations into C, remove overhead, that's it. There's a syntax, which you can use in Python code, so in the Python language, but that's also a syntax for pure Python, so you can just add kind of this decorator here to a function, to a Python function, and have it either execute in Python, or the decorator will just disappear, or have it compile in Python, and Python will understand the decorator. The cool thing is that you can employ this exactly where performance matters, so you can profile your code, take a look at where the real bottlenecks are, drop some type annotations in there, and make it way faster just by optimizing some bits of your code. Okay, and you would normally, so for highly computational code, you would normally expect a couple of hundred times speed up. Okay, so here's the demo. Yep, so this is the IPython notebook, for those who don't know. And so here's a really tiny little example, it actually makes this a bit bigger, that's maybe too big, okay, there we go. So a tiny example that just says, okay, this is the integer, I'm assigning it a value of one, two, three, four, and then printing it, okay? And you get what you expect, unless it doesn't work, and we restart the kernel, restart this, okay, try again. Yeah, works now, okay. So what this does is, you have a C integer and your, this is a Python, it's automatically creating an object for you, wrapping it in a Python object and passing it to the print function, okay? Okay, another example this year, you get in some sequence of values or an iterable of values, then iterate over it and sum it up, okay? Here's an example we pass in range-chen, that's, well, this is Python 3, so range-chen gives you an iterable over a zero to nine, pass into your function and it'll add it up at the C level, meaning here, in this iteration loop, it'll get the value from the iterator, unpack it into a C int, and sum is a C variable, C integer variable, do a C addition here and return it and this creates an object again from the C integer variable. You can take a look, and it actually works, and since I didn't just say a Python here, I said this is a Python minus A code, it drops out a little type analysis for you and shows you, okay, this was your code, this is how it made, and you can take a look at what it made of it, and this is the C code it generates just for the iteration, and the fun thing about it, it does a couple of optimizations, so if you can read the API code, then this is checking for lists, this is checking for tuple, which is kind of the most common two cases for iteration, I mean, like iterating over the list, that's what you do all the time, right? So it kind of has optimistic optimizations that say, if it's a list, do it in faster code, okay? And you can see this line here is white, yellow kind of means this is Python operations taken place, and white means this is like plain C, and anything in between is, well, anything in between. And you can see this is like a plain C operation, so it just takes some add value, signs up to some, okay? Read the code. Okay, and gets the right result. And this is all done automatically, right? So you just write down your code as you would in Python, you're saying these two variables are integer variables, and that's all you have to do, that just works. Okay, here's another example, so you get a character pointer from somewhere, so that's kind of the basic C string type. You can call len on it, which internally calls string len, and then print it, so that would call string len, get a size C value back, convert that to a Python object, so an integer object, and print it, and when you return the character pointer, it also knows that it needs to convert it to an object, so it converts character pointer to a byte string. Okay, and it works. Okay, encoding and decoding. Again, we have a character pointer, and we say, well, decode it as UTF-8, and you get a Unicode string back. Okay? It's kind of as you would expect, if it was not a character pointer but a byte string, the same would just work in Python, exactly the same way. This is a bit more inefficient than it needs to be, because C string, so character pointer strings in C don't have a length associated with them, they're just pointer, and so in order to figure out how much of the string we have to convert here, we have to call string len on it, and since I already know, so it calls string len, and then passes the results of the pointer and length into the decoding function internally, and since I already know how long the string is here, it's like seven characters, I can tell it. I can just slice the pointer, and that's more efficient, because there's no runtime stringing detection necessary anymore. Okay. One more thing here, what we got back is, so we had to do a manual decoding here, okay, what I want here is, not the byte string that I would normally get, but decoded by UTF-8, and then return the Unicode string, and you can automate that by saying, okay, what I actually want is not byte strings, I want Unicode strings, and the encoding for that is UTF-8, so this is again a compiler directive that you're passing in, and then you just have to say return S, and it'll return the decoded Unicode string for you, all automatic, okay? So this is kind of the comfort you get by the type system. Okay, here's another little example, I'm using the A2I function from the C standard library, and I'm using it to parse a byte string into an integer, okay, that's what it does. So what I do is I write a Python function that gets in some kind of byte string, I call A2I on the byte string, add one, return it, okay? Works, so I'm passing in a byte string here, I'm calling it with byte string as argument, I'm calling it with byte array as argument, both works, both queries to C character pointers, A2I gets the character pointer in, returns some C integer value, Python generates code that adds one, then you say return that, it converts the integer value to Python object returns it, okay? Okay, here's a C++ example, I'm using the standard library, the C++ standard library, the STL, and I'm using two objects from it, a string and vector, and what I'm doing it is I'm taking a byte string here, splitting it, this is like playing Python operations, which gets me a list, a Python list, assign it to a vector of strings, so that's a C++ code, it gets copied into a C++ vector for me, I can print the vector, which needs to do a translation back into Python so that converts the C++ vector into a Python list again, and prints it, I can just show you what it does, okay, we're running it, so printing here gets me the Python list of strings, and as you see, it's been decoded, as I asked it to automatically decode for me, and then I iterate over the vector, and whenever it's found, I find this little string here, this byte string in the vector, I print it out, so here you can see it's been found, and the next thing I'm doing is I'm passing in a position into the function, and I'm just indexing the vector, getting the whatever index value I want, returning that, and I'm calling it with index one, index zero, and it tells me that at index one, it's D, H, and scrolling down, at index zero, it's A, B, C, as expected, so back and forth, Python, C++, back into Python, anywhere you like. Callbacks, who's been using callbacks in Python? Okay, basic idea is you pass a function into some code, and you want the code to call your function, okay, at some point, whenever something happens, or for any item in this or something, that's the basic idea. So if you do that in C, it's a bit more verbous because, well, functions in Python have state, they have closures, and C does not have closures. All you have in C is pointers, and so the way callbacks work in C is you not only pass in your function, so a pointer to your C function, you also pass in a pointer to some state, some data somewhere, which is then, when the callback is called, pass into the callback so that the callback can make actual use of it, know what it has to do. Okay, otherwise you couldn't remember in what state it's supposed to process something, because C can't remember it. So the normal signature of callbacks in C is you pass in some void pointer which gives you a context, and, well, some more data the callback should be operating on. Okay, so here's a function that uses a callback. As I said, you pass in your function pointer and you pass in the context. Okay, so the void pointer that keeps the state for the callback. And then what I'm doing here is I'm passing in the character pointer, iterating over it, looking for the end, and whenever I find a small or uppercase A, I call my callback. Okay, so I'm running through byte string and then I find A, I call the callback and tell it here's a byte string, here's what I found. Okay, and then I'm implementing this callback and I'm saying, okay, the context I'm getting in is actually byte array. So what I'm passing through here is not an arbitrary void pointer, it's a pointer to an object, a Python object. And then whatever character was found, I'm pending to the byte array. Okay, so I'm collecting A's. Basically, that's the idea. Okay, and here's the function that calls it all. So I'm creating the byte array and then call my function, the processes some string, tell it whenever you find A's call this append character function and the context I'm passing in is a pointer to my byte array. Okay? Then when I call it all, I pass in the byte string and let the code operate on the byte string. And here it is, it found all A's. So what happened is I called this function here. It told this function, here's the callback, here's the byte array, do stuff. And the function called my callback and appended all A's it found. Okay? Okay, that's it for the general part. I have a couple of more and a bit more time. Okay, here's an example. I'll just restart that channel too. Okay? So here's an example of using structs in Cython, a C-struct. This is the way I would define a C-struct in my code. I'll say, so C-def is kind of the keyword that tells you, okay, here's a C definition coming next. And then I say I'm defining a struct called point. It has two double values, X and Y and an in-value color. It's a C-struct. Then I have a function here, a Python function that uses this struct and I'm creating the struct here first. And the cool thing is I can actually use object creation syntax. I can say create point for me, X is one, Y is two, color is 123 and it will assign the values to the right struct fields for me. Really nice syntax, I like that. But I really like it. Then I'm printing one of the values here. This should actually have parentheses. It doesn't matter because I'm writing a Cython through code, but it looks better. I'm running it and... yep, so I'm creating functions and then when I call it, it prints 1.0, which is double value of X and it returns a dict for me. Who would have expected that? What I returned was D struct and it's automatically turned into a dict for me. Which is kind of nice. It definitely helps with debugging when you can just say print the struct. It's kind of the one obvious way to represent structs because they are named value pair as more or less. This is how structs work in Cython. What else do I have? Again, a bit more time. I'll give you an example of our Unicode processing. I'll restart that too. Here's a little function that iterates over something in Python. We're not telling you what it is. I can tell you it's a Unicode string that I'm going to be operating on. I'm iterating over Unicode string, enumerating it, and then for any character, I find that it's numeric. The first character that I find is numeric, I return the position. Then I'm creating some dummy data here, string escalators, lots of times, and then putting 1, 2, 3 in there because that's what I'm going to be looking for. I can run the whole thing like there's a position way back in the string which has what I'm looking for. I can just run time it on it and see how long it takes. Kind of okay. Not too slow. Now I'm doing the same in Cython. What I'm actually doing here is I'm using Python syntax. I'm not using a Python CDF something syntax. I'm actually changing my Python code to stay Python code. I'm just telling Cython when you compile this code here's some type information that you can explore to make it faster. What I change is I'm importing the magic Cython module which is kind of a dummy module when you use it in Python but Cython understands it and goes I know what that module does. It has a function called locals which allows me to define types for local variables. And here I'm saying s so the argument I'm passing in is actually a stir so this is Python 3 so it's a unicode string and I'm saying that here is some weird little integer type which Python uses internally to represent a unicode character. So this PyUc4 thingy is there to you can put any unicode character in there it's an integer type that represents the unicode string the unicode character and the rest of the code is actually unchanged as before and so when I run this through the compiler it tells me I generated this code for it and it tells me well this is using some speedy C API code to find out if the character is numeric this does a bit more and the main thing it does is it doesn't do really much that many checks of s maybe none before it iterates because none doesn't iterate and then for turning I it has to convert it into a Python object again and the rest actually runs in C so here's a comparison time it on both which apparently runs for a while yep and it's a tad faster okay so the well I mean the major difference is it's the Python implementation runs in plain C and the other one runs in Python so the difference here is not entirely unexpected okay I mean this is milliseconds, this is microseconds yeah, factors okay that's pretty much what I wanted to show you as a demo now quickly about the high performance features that we added to Cython so yeah, part 3 so there's a couple of features that people use when they're doing large data processing when they're doing processing with NumPy data for example, NumPy arrays usually then what they want is a way to unpack these objects these NumPy array objects into something that they can process efficiently at a native level so they want to basically iterate over the arrays and do processing on them okay that's usually the main idea what people want and we have added a simple syntax for it which looks a lot like what you would do in NumPy like the normal NumPy slicing syntax and this is a syntax for unboxing low-level data that is kind of like the n-dimensional okay that includes one-dimensional bytes objects but also NumPy arrays, 4-term buffers, c-buffers image processing is done that way lots of applications to that and all you have to do is you have to say this variable here, this argument I'm getting in is actually a two-dimensional buffer so two columns here two arguments, of type double and that's it then you can iterate over it you can just say this is the size in one direction, this is the size in the other direction, I have a nested loop over it and all I'm doing here is adding one to each item in the array and this turns into c-code so a nice second feature of these memory views here is that they support efficient slicing so without changing this code you can just say, okay I don't want to run the code over the whole array I'm only interested in all even lines so every second line and you just slice it, pass it in done exactly as efficient it just recalculates the buffer layout in memory and runs the same algorithm over it okay, views typed next big topic it allows you to have one implementation with many specializations that's also very common in numerics that you want to write an algorithm once but have it run efficiently on lots of different integer types, floating point types like item types in arrays okay and you really just want to implement it once because it's hard enough to get it right once and you don't want to copy-code over so what we've added for that is what we call views types it's kind of compile time generics and the way it works is you say I'm defining a type, a views type which I call floating and it's made up by two different types two different c-types one is called float and the other one is double and the 64-bit float and then you use it in your code and Scython will just understand it and will say okay, so I'm getting the buffer in here but it's actually a views type buffer and so it will split up your code and generate two versions from it one that's optimized for 32-bit float and one that's optimized for 64-bit float you get that all automatic we have predefined types which are floating point types or integral types or numeric types but people use it with I've seen code that uses huge lists of different types for views types here and just writes an algorithm and it expands into like 30 different versions of the code if they need it, it didn't so far okay, next bit as I said this is really quick run over everything openmp, so parallel code that's another thing that people love in numerics they want to parallelize the code because they have huge amounts of data and they want to use as many cores as they have right, or more they always want to use more cores than they have but they can't so there's a way to have thread parallel loops and also thread parallel sections so this is not only usable for numerics it's usable for any thread parallelization in your code and all you really have to do is you replace the 4i in range in your loop by 4i in p range so that's a special site on parallel module that gives you this and then your code runs in parallel that's it what you should not forget is to free the gil so that you get both thread parallel code but again that's all you need to do so p-range free the gil and then you have c-code that runs in parallel on your machine okay so pretty much through for conclusion so siphon is a tool for translating python code to efficiency and it allows it to easily interface with native code I mean you're seeing that you're calling a2i is just like a2i you use it to speed up existing python modules so you can concentrate on the optimizations you don't have to rewrite everything in c or whatever you can just drop it into siphon optimize it a little see that it works fast and that's what you get without major changes in your code you can write c extensions for c python in python in actual python code you can just swap languages and you can wrap c libraries in python by just calling c functions right out of your code and implement a nice wrapper for it in python code so you can write a class called c code and it's the interface that you get so you can really concentrate on writing an efficient mapping for the c code to python code and you get a nice api and you don't have to care about too many details about low level stuff okay and siphon gets you all the way from python to c and what I really like about it is that you can use it as a parito language does everyone know what the parito principle is? the 80-20 principle so that's exactly the type of language it is it says you get 80% of the benefit with 20% of the effort and siphon really allows you to find the right 20%, do some stuff there and you get a huge speed up so it's dynamic and simple way it will stay dynamic and simple where you can in your code and just go steady and low level where you must so siphon supports fast code writing just as well as writing fast code thanks thank you stevan for this very nice talk we have 5 minutes for questions, there's a microphone next to the camera please come to the front can you hear me? how difficult would it be to include external c or c++ libraries not just the standard library for example boost or something else oh yeah boost boost is template driven so it's not entirely fun to do it but siphon has template support so you can declare a class and say this is a template class I don't think I have an example here so we have documentation on the page that tells you a couple of examples how you declare your code basically what you do is you say cdiffextern so this is an external declaration and then you just copy the definitions you say this is a class and these methods are in there c++ class with these c++ methods add a marker that's templated and that's also what I did here for the atoy example so the atoy example just works because we are shipping the declarations for the standard c library we are also shipping the declarations for the c++ stl containers so you can just use those right away if you have your own stuff you can declare them it's totally not much work you mostly just copy stuff from your header file declare it in the way siphon wants it and then you can just say import stuff use it regarding generics and fusing the types so you are supposed to use templates for c++ so this is the templates instead of defining your fuse types in siphon it's a different mechanism I don't think so because siphon has to understand these types somehow and templates don't really have a type yeah exactly so yeah no it's a different mechanism it kind of you can use it for the same thing but this works at the generics or code generator level and the other one would work in the c++ compiler level we are using siphon extensively in our machine learning library but one problem we have is we lose all the support for the static code checking for example using pylint on sizing files isn't possible we can use the decorators with the locals for example sizing.locals but for cdev classes or something else there's nothing similar or that also works so we have a syntax you can look it up in the documentation so there's a lot of the so actually most of the syntax is available at the python level but only the things that can actually be used from python or sensitively used from python so what you can't do is talk to ccode because there's no presentation for talking to ccode from python in the way siphon would allow it to do so there's no real mapping so if you use a python syntax for your siphon code then you just can't talk to ccode but that's about it everything else or other language features are available in pure python mode as well okay any more questions one more last one hello first thank you for the talk second I use siphon in the past and I had trouble between moving from linux to windows because simply the compilers were different I mean it was simply because one compiler was ignoring something like siphon generated which was senseless but the compiler ignored it and so I was just wondering if you have some kind of test suite to be sure that code emitted by siphon works on this precise compiler you have on this machine so we can have something like a limited warranty that it will work so as far as I understand siphon was about testing how do we assure that in fact the c generated was not quite incorrect but in the gray area and so MSVC was saying this is illegal which was one correct position and GCCU was saying something else but I said okay I ignore it it's senseless so most of the development of siphon itself is based on either Mac or linux so all developers are basically like no developer is working on windows so that's a call for help that is a call for help so if you have any windows based developers here which is kind of rare on a python conference but it happens there are really people doing serious development work on windows and so we can always use help there but as I said so the main development work so the core development work is happening on windows and Mac so we have GCC and C-Lang in the loop but we do not have MSVC in the normal development loop we just kind of test it after the fact so the people run it through there MSVC compilers test our test suite on their machines tell us if it works and usually it does not work before release so we fix it and when it goes out as a release it usually works there I don't know it's just an idea but maybe when we install siton we could have the option of running a test suite to be sure that the machine does work as because maybe not so many people are windows developers but maybe they use siton on windows and test it and just to be sure I have these settings and does it work you can run the normal test suite it's part of the source distribution okay then we are at the end thank you very much again