PDA

View Full Version : How about String Variables?



mytekcontrols
- 5th November 2005, 08:43
I didn't see this one listed, and it is something that most Basics do support, but not PicBasic.

It is rather annoying having to do stuff like this:


For x = 0 to 12
LookUp x,["Hello World!"],y
Buffer[x] = y
Next x


When if you had String variables, you could accomplish the same thing like this:


Buffer = "Hello World!"


Now which would you rather do :confused:

Of course this is just getting started down that long and "stringy" road (wasn't that a Beatles song?) :).

And hey! What about some of these other nice string functions (as he drools):

<div align="center"><table width="575" cellpadding="4" cellspacing="0" border="1" bgcolor="#FFF8C7"><thead><tr><td align="center" bgcolor="#004BB0"><font color="#ffffff"><b>Function</b></font></td><td align="center" bgcolor="#004BB0"><font color="#ffffff"><b>Description</b></font></td></tr></thead><tbody><tr> <td><tt>Chr(int)</tt></td><td>Returns the ASCII character of the argument.</td></tr><tr><td><tt>InStr([start,] str1, str2)</tt></td><td>Returns the starting position of <tt>str2</tt> within <tt>str1</tt>, beginning at <tt>start</tt> if specified.</td></tr><tr><td><tt>InstrRev(str1, str2 [,start])</tt></td><td>Returns the starting position of <tt>str2</tt> within <tt>str1</tt> from the end of the string, beginning with <tt>start</tt> if specified.</td></tr><tr><td><tt>Join(list [,delimeter])</tt></td><td>Merges strings in the list, each separated by one space unless you specify a <tt>delimeter</tt> character.</td></tr><tr><td><tt>LCase(str)</tt></td><td>Returns the argument in all lowercase letters.</td></tr><tr><td><tt>Left(str, int)</tt></td><td>Returns the far-left <tt>int</tt> characters from the argument.</td></tr><tr><td><tt>Len(str)</tt></td><td>Returns the number of characters in the string. (Notice that <tt>Len()</tt> works on numeric arguments as well.)</td></tr><tr><td><tt>LTrim(str)</tt></td><td>Returns the string argument, without leading spaces.</td></tr><tr><td><tt>Mid(str, intStart [, intLen])</tt></td><td>Returns a substring of the argument, starting with the character at <tt>intStart</tt> and continuing until the entire the string is extracted or until the optional <tt>intLen</tt> characters have been extracted. <tt>Mid()</tt> is called the midstring function because it can return the middle portion of a string.</td></tr><tr><td><tt>Right(str, int)</tt></td><td>Returns the far-right <tt>int</tt> characters from the argument.</td></tr><tr><td><tt>RTrim(str)</tt></td><td>Returns the string argument, without trailing spaces.</td></tr><tr><td><tt>Str()</tt></td><td>Converts its numeric argument to a string with the numeric digits in the string.</td></tr><tr><td><tt>StrReverse(string)</tt></td><td>Returns the string argument that is completely reversed.</td></tr><tr><td><tt>UCase(str)</tt></td><td>Returns the argument in all uppercase letters.</td></tr></tbody></table></div>

Now before someone jumps down my throat, and says something like "well this and that really isn't needed" or "you know you can write some code to simulate that". Remember this is a WISH LIST, and I like to make BIG wishes :rolleyes:

gtvmarty
- 29th September 2009, 04:23
I'm in 100% agreement...perhaps the "One" thing i hate about picbasic is the severe lack of string-handling.
I would LOVE something as simple as a string buffer/variable to contain a value such as "Hello" etc.

In the meantime, who out there has other solutions for storing/handling strings???

I'm sure there's a few ways to store the string (as bytes) in the internal Ram and retrieve them as concatenated strings...

May your WISH come true :)
Marty.

mackrackit
- 29th September 2009, 06:01
I am curious about the application using a MCU where that type of string handleing :) would be needed?

The serial and LCD commands can work with strings via arrays now.
I see most people using a MCU for monitoring/control of something. Just wondering what you have in mind?

gtvmarty
- 6th October 2009, 09:40
Who would waste time with code-consuming arrays when you could use something like "Buffer = "Hello World!" as shown above.

In recent coding i've played with, i've had the need for using 2character strings, it would seem inefficient to use several for-next arrays for only 2 characters.

Why can't a buffer be assigned to xx-amount of ram locations and be given a name or symbol that can be called upon by name?

Don't get me wrong, Picbasic Pro is EXCELLENT, but has a LONG way to go ;)
I can't believe many of the simple tasks i used to do with GWbasic 25+ years ago, still isn't supported in Picbasic....

Marty.

mackrackit
- 6th October 2009, 14:02
Don't get me wrong, Picbasic Pro is EXCELLENT, but has a LONG way to go ;)
I can't believe many of the simple tasks i used to do with GWbasic 25+ years ago, still isn't supported in Picbasic....

Marty.
Wow! I did not know that GWbasic worked with the architecture of a MCU. :)

gtvmarty
- 7th October 2009, 03:32
U must have had the older version :D


Wow! I did not know that GWbasic worked with the architecture of a MCU. :)

circuitpro
- 10th October 2009, 19:43
Strings are needed ALL THE TIME! I wish PBP would face the music and implement them! Every other compiler I see has them, why not PBP? (And No, I don't want to switch because I've got years and $ invested in this one.)

mackrackit
- 10th October 2009, 19:55
Strings are needed ALL THE TIME! I wish PBP would face the music and implement them! Every other compiler I see has them, why not PBP? (And No, I don't want to switch because I've got years and $ invested in this one.)
Like I asked before, what type of project would you do that needs this?
I am not trying to be difficult, I just have not had the need beyond what is already there.

It might be fun/interesting to write a library/include to handle some of this stuff. If an example of it's usage was given I might give it a go...

circuitpro
- 11th October 2009, 05:00
Well, my last embedded project was a remote control for some specialty plasma monitors. The language used in the remotes uses exclusively strings for control. As I said before in another post, strings are a fact of life, get over it. Just as numbers, and variables, strings are used in everyday life and becoming more and more necessary for any kind of efficient programming. Rediculious little FOR...NEXT loops to parse out a string character by character are S T U P I D and a waste of time. IMO. Words are a fact of life. Adjust.

Darrel Taylor
- 11th October 2009, 06:33
Now that the "Warm Fuzzies" are flowing ... :)

With PBP 2.60 ...
The ARRAYREAD and ARRAYWRITE commands are quite useful for dealing with Strings.
<br>

circuitpro
- 11th October 2009, 19:02
Sorry, I just feel so strongly about this and struggle every time a new project comes up because of these features lacking in PBP. I guess I'm a ranter! Sorry.

Charles Linquis
- 18th October 2009, 21:11
I, too would like strings. I create extensive menus and text messages. Some of the same messages have to be output to

1. Serial Port (uses HSEROUT
2. Network Port (uses HSEROUT2)
3. LCD

Even with arrays, it is hard to output hundreds of different messages to each. Now, I duplicate the messages. Not code efficient.

tr6coug
- 11th November 2009, 01:33
Like I asked before, what type of project would you do that needs this?
I am not trying to be difficult, I just have not had the need beyond what is already there.

It might be fun/interesting to write a library/include to handle some of this stuff. If an example of it's usage was given I might give it a go...

Here's an example and something that I'm working on now. I am debugging a program that will read 3 axes of an accelerometer via A/D conversion. The sampled value is a number and I'd like to convert it to an array of text that I can send to the USB port. The pic is enumerating as a USB serial port so I can read it via a terminal program.

Anybody know an efficient way to convert a 10-bit value to a number string?

Darrel Taylor
- 11th November 2009, 02:33
Anybody know an efficient way to convert a 10-bit value to a number string? Sure ... If you have PBP 2.60?
ARRAYWRITE MyArray,[DEC4 MyValue]

Or for 3-axis ...
ARRAYWRITE MyArray,[DEC4 Xaxis, DEC4 Yaxis, DEC4 Zaxis]
____________
DT
http://www.pbpgroup.com/files/SIGIMG/Upgrade.gif

tr6coug
- 11th November 2009, 04:18
OK, cool.

Looks like its a good time to pay my $25 and upgrade from 2.46. BTW, I used your interrupt based USB servicing include file. Works great. Thanks for sharing that.

Darrel Taylor
- 11th November 2009, 06:30
Looks like its a good time to pay my $25 and upgrade from 2.46.
http://www.picbasic.co.uk/forum/images/icons/icon14.gif &nbsp; I should start charging meLabs a commission on upgrades. :)

BTW, I used your interrupt based USB servicing include file. Works great. Thanks for sharing that.

Excellent! You're Welcome! and thanks for the "User Review" ... :D

Cheers,

polymer52
- 30th January 2010, 12:20
Like I asked before, what type of project would you do that needs this?
I am not trying to be difficult, I just have not had the need beyond what is already there.

It might be fun/interesting to write a library/include to handle some of this stuff. If an example of it's usage was given I might give it a go...

I send a lot of strings to LCD's and some of the "STD" basic string functions would be really handy. Of coarse strings suck up a bunch of memory but Constantly used strings can be stored in EEProm and called when needed.

LGabrielson
- 19th August 2011, 01:58
Only 2 years later and still no string commands. Left(2), Right(3), etc. I'm almost too old to code anymore... so much for waiting around.

mister_e
- 19th August 2011, 02:32
I do understand what you mean, I can't argue against however...
What's a string? A couple of consecutive character
What's an Array? A couple of consecutive Byte
A single Character = Byte
Array are supported, therefore Strings are.

ArrayWrite/ArrayRead, a spoon of creativity and you're all set. Sprinkle a macro in the equation (for ease of use) and VOILA!

Demon
- 19th August 2011, 03:52
ARRAYREAD/WRITE, I guess it's time for me to upgrade as well. :)

circuitpro
- 20th August 2011, 02:15
No kidding. Good thing we weren't holding our breaths! It isn't that it can't be done some other contrived way, it just seems like the effort should be put forth to implement some of BASIC's functions to make it easier for people familiar with BASIC, and simply get the job done. My last comment here was Dec 2008. Still waiting.

rmteo
- 27th September 2011, 18:04
This has everything you want http://www.sfcompiler.co.uk/swordfish/ only for PIC18 though. The author hangs out here from time to time.


Swordfish is a highly structured, modular PICŪ BASIC compiler for the PIC18 family of PICŪ microcontrollers. Swordfish is a true compiler that generates optimised, stand alone code which can be programmed directly into your microcontroller. Extensive library support is provided (http://www.sfcompiler.co.uk/wiki/pmwiki.php?n=Swordfish.Modules) with full source code, some of which include LCD, GLCD, EEPROM, ADC, software and hardware SPI, software and hardware I2C, software UART, USART, string manipulation, USB and math libraries. Support for strings, arrays, structures, boolean, bit, unsigned and signed 8, 16 and 32 bit ordinal types and 32 bit floating point is also provided. Swordfish is supplied with a powerful and flexible Integrated Development Environment (IDE) which includes an advanced code explorer, full syntax highlighting, third party programmer integration, serial communicator and integrated boot loader application. Just a single mouse click, or key press, will compile, assemble and program your project into the target microcontroller.

Charles Linquis
- 28th September 2011, 07:10
Isn't it too bad that we can't get MELabs, John David Barker and MikroE to REALLY work together. Just think of all the good code that would come out. Perhaps if we bought them plane tickets and a few beers...

I'll pitch in!

mister_e
- 28th September 2011, 07:48
Not at all, if people can't make some effort by themself, too bad for them.

There's an alternative for each problem... stop moaning, work and learn from, or GT*O :D

Heckler
- 28th September 2011, 17:41
Yea, and those who wanted to go faster than the Horse and Buggy should have just kept quiet and enjoyed the slow meandering ride:rolleyes:

Nothing wrong with wishing for (and expecting) improvements to an allready great compiler... that is the reason PBP3 is out.

Dwight

mister_e
- 28th September 2011, 21:36
I agree that common string manipulation things should have been there, but Melabs decided to not do it and be like a BasicStamp alike replacement. Nothing wrong with it. Sometime you need to draw the line somewhere... they did.

There's some hope I guess.

Charles Linquis
- 28th September 2011, 21:56
There are some pretty good work-arounds for strings already. Try

Arraywrite TestString,["This is a pretty good way to load strings into an array",0]

Hserout [Str TestString]

mister_e
- 28th September 2011, 22:10
This is exactly what I said at post 19 :p

Charles Linquis
- 28th September 2011, 23:09
Well - EXCUUUUUUUUUUUSE me. My screen is not "tall" enough to see post 19 and 27 at the same time. And I have a short attention span as well.

Demon
- 29th September 2011, 01:27
Mine can be if I put all 6 monitors vertically in portrait mode. LMAO!

Robert
:P

bogdan
- 29th September 2011, 01:55
you have a high ceiling house Demon...

mister_e
- 29th September 2011, 02:05
OR small screen ;)

Heckler
- 29th September 2011, 05:26
OK Now that last string of posts are just about the funniest thing I have read around here in a while.

Or else I am just so tired that I need to get some sleep... Been chasing satelite outages for the last 14 hours and about 500 miles. Spacenet decided to change frequencies in the middle of last night and I woke up to 16 sattelites down. (we use them to bring in electricity load information on a 5 minute basis from major substations in Utah, Wyoming and Nevada.

Probably just tired...

You guys and gals are a great bunch.

LGabrielson
- 3rd November 2012, 04:11
Yeah, how about STRING variables?????


Strings are needed ALL THE TIME! I wish PBP would face the music and implement them! Every other compiler I see has them, why not PBP? (And No, I don't want to switch because I've got years and $ invested in this one.)

rmteo
- 3rd November 2012, 06:20
There are many other features besides strings available in other compilers that are not available in PBP.

Charles Linquis
- 5th November 2012, 01:03
I like strings as well. All my projects have either RS-232 or Ethernet connectivity, and they all have
menus, user input, etc. I need to handle strings constantly.

I generally put all my strings into arrays. This is the only way they can be manipulated. Check out
the ARRAYWRITE command. It is an easy way to load and clear arrays.

Here is some stuff that I cobbled together that may help the situation. Note that I pulled this from
various programs, so it has not been tested completely, although most routines should work as-is.
Use at your own risk!

Lester may want to move this into the 'code examples' section later.




;----------------------------------------------------------------------
IgnoreCase var bit
Xs var byte
Ys var byte
Zs var byte
Js var byte
Ws var byte
Cmd var byte
Length1 var word
Length2 var word
MaxSearchLen var byte

Str1ArrayLength CON 30
Str2ArrayLength con 30

MaxCompareLength var byte

Str1 var byte[Str1ArrayLength]
Str2 var byte[Str2ArrayLength]
;---------------------------------------------------------------------------
; Zero out the strings
arraywrite Str1,[rep 0\Str1ArrayLength]
arraywrite Str2,[rep 0\Str2ArrayLength]

;--------------------------------------------------

; Fill input arrays with some test stuff

ARRAYWRITE Str1,["0123456789ABCDEF"]
arraywrite Str2,["345"]
;-------------------------------------------------

'LeftString: ; Str2 contains the leftmost chars of Str1
; Ys = Number of Chars to separate off from Str1
YS = 6 ; for testing only


If Ys != 0 then
For Js = 0 to YS-1
Str2[Js] = Str1[Js]
next Js
Str2[Js] = 0 ; make certain we always end with a nul
endif
return
;----------------------------------------------------
;----------------------------------------------------

MidString: ; Str2 contains a string of Ys chars derived from Str1 starting at position Zs (zero based)

ys = 2 :Zs = 4 ; for testing only

If Ys !=0 then
For Xs = Zs to ((Zs + Ys)-1)
Str2[Xs-Zs] = Str1[Xs]
next Xs
Str2[xs] = 0 ; terminate with a nul
Endif
return

;-------------------------------------------------------------------------------------

RightString: ; Str2 contains a string consisting of the right-most chars (number defined by Ys) of Str1

Ys = 4 ; for testing only

Js = Ys - 1
for xs = Str1ArrayLength to 0 step - 1
If Str1[xs] != 0 then
Str2 [Js] = Str1[Xs]
if Js = 0 then DoneRS
Js = Js-1
endif
next xs
DoneRS:
; return
;---------------------------------------------------------------------------------------
LenSTring: ; Ys contains the length of STR1
Js= 0
While Str1[JS] != 0
JS = Js + 1
wend
if Js > 1 then
Ys = Js - 2
else
YS = 0
endif
; return
;----------------------------------------------------------------------------------------
InString: ; Ws contains the position in STR1 where the match with STR2 started
; if there is no match, Ws contains 255
; if Ignore Case is set to '1', it will, of course, ignore the case.

MaxCompareLength = Str1ArrayLength Min Str2ArrayLength
for Xs = 0 to Str1ArrayLength
If IgnoreCase then
Str2[0] = Str2[0] & %11011111
Str1[Xs] = Str1[XS] & %11011111
endif
If Str2[0] = Str1[Xs] then ; Found a match on 1st char of Str2

Ws = Xs
Js = 1
for Ys = Xs + 1 to MaxCompareLength
if Str2[Js] = 0 then FoundMatch ; hit the end of Str2
If IgnoreCase then
Str2[Js] = Str2[JS] & %11011111
Str1[Ys] = Str1[Ys] & %11011111
endif
if Str2[Js] != Str1[Ys] then NoMatch
Js = Js + 1
next Ys
goto FoundMatch
endif
next Xs
NoMatch:
WS = 255
return
FoundMatch:
return

;-----------------------------------------------------------------------------------
CommandParser:
; Fast searcher! Case sensitive. Closely based on info provided by Darrel Taylor.
; Searches through Str1. If it finds the strings listed in the quotes, it returns which one it found.
; the max length of STR1 that will be searched is in MaxSearchLen

Cmd = 255 ; In case of no match
MaxSearchLen = 20

Parse01: Cmd=01 : ARRAYREAD Str1,MaxSearchLen,Parse02,[WAIT("Command1")] : GOTO Foundit
Parse02: Cmd=02 : ARRAYREAD Str1,MaxSearchLen,Parse03,[WAIT("Age")] : GOTO Foundit
Parse03: Cmd=03 : ARRAYREAD Str1,MaxSearchLen,Parse04,[WAIT("Weight")] : GOTO Foundit
Parse04: Cmd=04 : ARRAYREAD Str1,MaxSearchLen,Parse05,[WAIT("Address")] : GOTO Foundit
Parse05: Cmd=05 : ARRAYREAD Str1,MaxSearchLen,Parse06,[WAIT("Command5")] : GOTO Foundit
Parse06: Cmd=06 : ARRAYREAD Str1,MaxSearchLen,Parse07,[WAIT("Command6")] : GOTO Foundit
Parse07: Cmd=07 : ARRAYREAD Str1,MaxSearchLen,Parse08,[WAIT("Command7")] : GOTO Foundit
Parse08: Cmd=08 : ARRAYREAD Str1,MaxSearchLen,Parse09,[WAIT("Command8")] : GOTO Foundit
Parse09: Cmd=09 : ARRAYREAD Str1,MaxSearchLen,Parse10,[WAIT("Command9")] : GOTO Foundit
Parse10: Cmd=10 : ARRAYREAD Str1,MaxSearchLen,Parse11,[WAIT("Command10")] : GOTO Foundit
Parse11: Cmd=11 : ARRAYREAD Str1,MaxSearchLen,Parse12,[WAIT("Command11")] : GOTO Foundit
Parse12: Cmd=12 : ARRAYREAD Str1,MaxSearchLen,Parse13,[WAIT("Command12")] : GOTO Foundit
Parse13: Cmd=13 : ARRAYREAD Str1,MaxSearchLen,Parse14,[WAIT("Command13")] : GOTO Foundit
Parse14: Cmd=14 : ARRAYREAD Str1,MaxSearchLen,Parse15,[WAIT("Command14")] : GOTO Foundit
Parse15: Cmd=15 : ARRAYREAD Str1,MaxSearchLen,Foundit,[WAIT("Command15")] : GOTO Foundit

Foundit:
return

;-------------------------------------------------------------------------------------------

ofuzzy1
- 12th January 2013, 20:53
Thanks Charles for your subroutines.

This is a snippet of code from a two temperature probe controller, chiller and alarm
Alarm MIN and MAX
Chiller MIN and MAX

Crammed it into a 12f1822, should of used a 12f1840 :p for more space.

Use gosub Print_Phrase: with zb pointing to the string.

eg zb= Ofuzzy1 : gosub Print_Phrase

will print out the copyright string:
(C)2011 Ofuzzy1 All Rights Reserved - Not for Life Critical Use!


Was cramped for space so phrases were pieced together.
MIN Alarm would be pieced together
Print_Alarm_Min:
gosub Print_Alarm: gosub Print_Min:
return
'================================================= =====================================




; Strings are stored in the EEPROM leaving more room for Programs
; The last char of the string has $80 added to it mark then end of the string.
; this simplifies string maintenance.
; Strings are sent out via DEGUG for Async Serial - RS232
; Debug is smaller than serout and serout2

' print character defines
CR con 13
LF con 10
VTAB CON 11 ' VERTICAL TAB = Clear Screen
DEGREE CON $DF
Dash con 45
Space con $20

' ---- The temps they do all the grunt work with little or no pay :)
AB VAR BYTE ' TEMP
BB VAR BYTE ' TEMP
xb var byte ' temp
yb var byte ' temp
zb var byte ' temp

' These are stored on the EEPROM Data area -- Leaves room for bigger programs
' *** Mark the end of the words with Char + 128
; @$10 starts the strings at location $10 [16] in the eeprom
CharBump con $80
Ofuzzy1 data @$10, "(C)2011 Ofuzzy1 All Rights Reserved - Not for Life Critical Use", "!" + CharBump
W_Alarm Data "Alarm", "_" + CharBump ; Alarm
W_Min Data "Mi", "n" + CharBump ; Min
W_Max Data "Ma", "x" + CharBump ; Max
W_Normal Data "Norma", "l" + CharBump
W_Both_Probes Data "Both Probe", "s" + CharBump
W_Enter Data "& Ente", "r" + CharBump
W_YNRQ Data "Y=Yes R=Redo N=No Q=Qui", "t" + CharBump

'================================================= ==========================


; begin MAIN program code

Gosub Print_Copyright
End
;end MAIN program code

===========
Print_Copyright:
gosub CR_LF3 ': gosub CR_LF
zb= Ofuzzy1 : gosub Print_Phrase
gosub CR_LF3 ': gosub CR_LF
return
'================================================= =====================================
Print_Phrase: ' Call with zb = ROM_Memory_Location [byte] last char is >$80
bb = 1 ' this could be re-written slicker but it works.
Do until !BB ' = 0
read zb, ab
if ab > CharBump-1 then
ab = ab - CharBump
bb = 0
endif
debug ab
zb= zb + 1
loop
return
'================================================= =====================================
CR_LF3: ; 3 line feeds
debug LF,cr
CR_LF2: ; 2 line feeds
debug LF,cr
CR_LF:
debug CR,LF ; 1 line feed
return
'================================================= =====================================
Print_SP2:
debug " "
Print_SP:
debug " "
return
'================================================= =====================================
Print_LT:
gosub Print_SP : gosub Print_lt1: gosub Print_SP
return
'================================================= =====================================

Print_LT1:
debug "<":
return
'================================================= =====================================

Print_GT:
gosub Print_SP : gosub Print_GT1: gosub Print_SP
return
'================================================= =====================================

Print_GT1:
debug ">":
return
'================================================= =====================================

Sean_Goddard
- 1st November 2014, 00:58
Well, BANG goes my idea of making a Ethernet controlled device using PICBASIC to deliver the menus etc.

I was an early adopter of this compiler, but to do what I need I'm moving over to Mikro C (SORRY) But it has string handling by the bucket load.

My latest project included a 3 way Bluetooth comms using 3 HC05 modules. The only way I could get them to talk was to do an AT+RNAME? to ID each unit, thus ensuring that the master had connected to the correct two units assigned to it (in the environment where this device will work there will be 100's of similar units and at pairing the master needs to know that is has one of each TYPE connected rather than relying on an "IP" address.

rmteo
- 18th January 2015, 05:02
...but to do what I need I'm moving over to Mikro C (SORRY) But it has string handling by the bucket load.

This post will probaby be deleted (like skimask's entire thread) for mentioning the advantages of a superior competitive product.

mark_s
- 19th January 2015, 18:02
Skimask's post wasn't deleted?

http://www.picbasic.co.uk/forum/showthread.php?t=19652

I agree, MEL needs to step up.

Sean_Goddard
- 20th January 2015, 13:53
AHA!!! I have found a way to do what I need using the variable pointer and a bitwise comparison of the "paired" addresses. OK it's not very fast but as it is only needed as initial system turn on, I am good to go. I wont post the code here yet as I need to test it in a variety of scenarios (and I need permission from for the customer). Seems to work well though.

Superior in some ways ONLY not entirely. Each language has it's benefits and uses, heck I write in C, PICBASIC and ASM, it depends on what I am trying to achieve.