Wednesday, July 26, 2017


--- Transportation in Denver ---


                                               The Rockies from my new neighborhood



                                                                       Water fowls

Part of my adventures since moving to Denver has been learning to get around. I won't say that it was my plan to get rid of my van and become a pedestrian - it pretty much just happened, but, most of the time I don't regret it.

The Dewey Decimal System includes transportation and communication in the social sciences for a reason. They are a major part of society. They are a large part of how people interact because they bring people together. Sometimes they bring people together too effectively because it has become so easy that folks take it for granted and when the technology fails them, they are often left floundering.

But I've really enjoyed learning the transportation system around here.

I didn't own a car until I was 27. Where I lived (Auburn University), I could walk anywhere I wanted to go and I enjoyed walking, but when I started picking up responsibilities all over the map, the car became useful.

When I moved to Denver, I couldn't get the resources I needed to get  my van registered. Emission testing is a big thing in Denver and I certainly recognize why. They've had a lot of problems with pollution. Most of that is in the past now because of things like emission testing. Good for them, no so good for me. But, I repeat, I like walking and Denver is a great place to walk.

But walking limits me to as far as my feet can carry me and only the distance they can carry me in a day. There's a lot more of Denver than that, so when we moved to South Harvey Park, I decided to learn to use the RTD (Regional Transportation District) buses and trains. My first bus trip was a long one from home to my eye doctor in Broomfield and back for a postoperative examination after one of my cataract surgeries. It went of well and day pass books aren't too expensive, so I made it a regular part of my travels.

I also noticed that there were inexpensive charter buses to places in the mountains like Black Hawk and Central City.

Throw in the light rail and most of my transportation problems were solved. Now, I know how to get just about anywhere I want to go. If I need to take a trip across the country, I can rent a car.

There's a bus stop right in front of the new place and I can easily walk to the nearest light rail station. The bus that runs in front of the house makes a beeline to City Park and the zoo and Museum of Nature and Science.

From Harvey Park, my trip to the "big Walmart" was either a day long trek up Bear Creek and South Platte River Trail to Dartmouth Avenue, across the river and up the valley in back of the Walmart; or it involved walking to River Point shopping area along Bear Creek and through the shopping center to Oxford Station. From there, I took a train to Englewood Station and walked the rest of the way (a short walk from there) to Walmart). This was a monthly practice.

It's a fair distance from where I live now to the same store but I decided to try it out the other day and was pleasantly surprised. It's all downhill and the walk is nice without a lot of street traffic. There are some nice parks and the walk ends in a shopping area with the post office, some favorite store, my favorite restaurant (The Beirut Grill), and the light rail station.

I used this trip to learn to use Lyft, a phone based taxi service operated by people who work for themselves and use their own vehicles. The process went off without a glitch, it was easy and inexpensive, and I learned another link in the local transportation system.

But now, I'll have to see what it's like for the second leg of the walk - that one is all uphill!



--- Intelligence 4 ---

Francine Coss' offering, "Developing and Assessing the Intelligence of a Kindergartner: A Practical Approach" is an interesting read, but it doesn't have much for the adventurer - unless you have a kindergarten age child.

But the next time you're in a grocery store, pick up a 3 pound bag of something. That's about the weight of an adult human brain. Somehow, that three pound mass of tissue creates your world for you.

Linda Baker's section has some interesting experiences. Oriented toward children and groups, the exercises are pretty easily adapted to adults either alone or in groups. There is an old book called Mind Games: The Guide to Inner Space by Robert Masters and Jean Houston (2nd ed. 1998. Quest Books) may be more appropriate for adults and I have put it to good use, but I would suggest being selective. There are some "opening" exercises in there that provide a guided exercise involving opening up to external entities. I, personally, consider that dangerous. Some folks think it's trivial because such entities (they think) are just psychological constructs. Others think it's okay because the universe is good and nothing is going to hurt you. I've had too much experience with entities that are "out there" and will hurt you. Be forewarned.

It's interesting that this comes up at a time that I'm specifically looking at the contemplative groups in the area. Typically, I'm not too comfortable with New Age philosophies but there is something to say for contemplative practises. It's pretty well established that meditation and similar practices do improve cognitive functions and I have written before on the practice of walking meditation to enhance learning on the trail. Also, there's no question that stretching and breathing exercises are great tools for hikers.

Although Ms. Baker talks about avoiding New Age practices in public school settings, even this section is a little too New Agey for me. For instance, I seriously question that "Love is all there is." Love is a lot of it - but you gotta eat sometime.



--- Intelligence 3 ---

Afolabi James Adebayo's section, "Teaching Awareness of Human Development" is a good read about sleeplessness and stress, especially for people having problems in those area. There's even information about why people might want to be stressed - some games people play.

Me? I rarely have problems with sleep. I learned to sleep in one of the noisiest environments I can think of - a lay barge. About the only time I have any real problem - it's a cosmic law that Wolf doesn't get to sleep the night before a long hike. My body knows what I'm going to do to it and does everything it can to sabotage my plans.

I will say that lack of sleep effects the results of psychological tests. I got to the point, as an evaluator, that if I saw my client dozing (sometimes, they would literally fall out of their chair), I would send them home and tell them to get a good night's sleep before they came back. And as the author of this piece indicates, lack of sleep interferes with both mental and physical performance.



--- Intelligence 2 ---

Again, Cynthia Wooding's module, "Self-Fulfilling Prophecy in African American Students" presents group activities to help African American students counteract biases in standard American educational systems by highlighting role models from the African American community that exemplified the analytical, creative, and practical approaches to intelligence.

The culturally biased testing issue was still going strong while I was a professional vocational evaluator and I'm not so sure it was ever resolved. I did give culturally unbiased test, because they were requested by counselors. And, yes, there was a cultural bias problem with the "classical" tests. Unfortunately, the culturally unbiased tests were lame.

The ones that I had to give produced minimal useful information that made me ask, "What am I measuring here?" Again, I dutifully gave the tests, along with informative additional tests and drew lots of useful information from extensive interviews, and I thoroughly deprecated the tests that I had to give in the report.

One counselor was in love with an scholastic aptitude test that had a reading level about 6 grades above the reading abilities of the clients I was testing.

(Sigh)

In the end, there are no culturally biased (or culturally unbiased) tests. There are only culturally biased evaluations performed by individuals who don't know how to design and interpret individualized evaluations.



--- Intelligence ---

Being a psychologist and retired vocational evaluator, I have what may seem like an embarrassing confession to make. I don't know what "intelligence" is, but I suspect that no one else does either - at least not in any precise way. I'm certainly not sure what an IQ is good for.

I've given many IQ tests, to be sure. I generally gave them for two reasons. First, the schools required them, but I had choice words for that requirement in my reports. That anyone could use the results of a test that can be so drastically influenced by indigestion for purposes of placement - to decide the future track of a person's life - boggles my mind.

I was disillusioned by IQ tests early on. My first client, in fact, was a charming young lady who chatted with me in a witty and smart manner as I stared at her profile that assured me that her IQ was 65. That's a heck of a way to break a vocational evaluator in. After the intake interview I went straight to the behavioral specialist and said, "This can't be right." After a brief interview with the client, the behavioral specialist returned and said, "You're right. I'll retest her." And sure enough, her IQ was 65!

I never trusted an intelligence quotient again.

But I won't say I didn't like IQ tests. My favorite ones were the ones that gave multiple scores. I used them in a more straight forward fashion. Instead of trying to get a blanket score to tell me how well people could solve problems, I looked at the individual scores to see how well people could solve those specific kinds of problems, and then I compared them with scores from other tests, and more importantly, I compared all those scores with what the person had done with their life - their successes and failures, their interests and their dreams, and I pulled all that together into a narrative. No one score could have ever satisfied me when the object under my scrutiny was anything so complex and magnificent as a person.

Uh....there is one other reason I liked IQ tests. They are fun. I like puzzles, so, obviously, I liked IQ tests.

Over the next week or two, I will be reading the Yale-New Haven Teacher's Institute's unit on Human Intelligence: Theories and Developmental Origins (http://teachersinstitute.yale.edu/curriculum/units/2001/6/ last accessed 7/18/2017), and then I'll see if I can find an IQ test that I haven't taken and given so many times I already know the answers by rote (and I'll recommend some that you can take yourself). I expect to have fun.

The first section was written by Dina Pollock and focused on intrapersonal intelligence, which is one of the intelligences mentioned by Howard Gardner in his work on multiple intelligences. Intrapersonal intelligence somewhat calls into question the classical idea that personality is the characteristics that are fairly stable in a person over their lifetime. A skill  included in intrapersonal intelligence is the ability to know self and to guide the development, to some extent, of self.

Most of the exercises suggested by Ms. Pollock are group exercises, which is reasonable since the section is about knowing self and we learn much of what we know about ourselves from other people. But some of the exercises are amenable to individual self exploration. Be careful with "Know your potatoes" if you don't live alone.

I'll add to Ms. Pollock's suggestions the Berkeley Personality Inventory, available in the book "Who Do You Think You Are?" by Keith Harary (the 2nd edition was published in 2005 by Penguin Putnam Trade). This brief personality inventory allows you to explore the way you see yourself (perceived personality) and the way you would like to be (ideal personality), and you can even let others score the inventory for you to see how they perceive your personality (if you dare).

I'm working on a guide for professionals working with therians and I have just finished reviewing a report by the International Anthropomorphic Research Group (Roberts, S. E., Plante, C., Gerbasi, K., & Reysen, S. (2015). Clinical interaction with anthropomorphic phenomenon: Notes for health professionals about interacting with clients who possess this unusual identity. Health & Social Work, 40(2), e42-e50). In their study, they found that furries (people who belong to a culture based on anthropomorphic characters and/or art) use alternate personas to move from the way they see themselves closer to they way they want to be.

I would expect the players of RPGs (Role Playing Games) could use characters created during play to do the same thing. But, then, I know a lot of gamers who intentionally choose characters that are not at all like themselves and are not at all like who they would want to be, as a kind of challenge.

I suspect that RPGs present a very fertile ground for psychological and sociological research.



--- Notes on knowledge ---

Thus the faculties of consciousness, of memory, of external sense, and of reason are all equally the gifts of nature. No good reason can be assigned for receiving the testimony of one of them, which is not of equal force with regard to the others.

Thomas Reid

There is a huge difference between Descartes' epistemology and our epistemology. A lot of our epistemology has drifted from philosophy to science - cognitive science. And science has worked out techniques for research that reduce uncertainty and bridges the gap between what we perceive and what is actually "out there".

Some still are suspicious of "faculties of consciousness" like intuition, emotion, and aesthetic judgment but these abilities developed with our race to take care of situations important to our survival and they are as important today as they ever were. We are often called to make "snap judgments" in situations where there is no time for long deliberation and many of our cognitive abilities are there for those situations.

But it is, nevertheless, important that our "faculties of consciousness" and, also our more respected faculties be trained to work well and to work together. Intuition, for instance, with attention and collaboration with reason can be fine tuned to be an effective and reliable tool for assessing situations that are "fuzzy", open to multiple interpretations, or that require a quick, cool summation.

I still run into people who think that memory is like a video recording in which everything that we've done or perceived is stored in our brains (if only we could get to the recordings). That idea has been disproved over and over by cognitive scientists who know that memories are reconstructions from very summary clues stored in our brains. We reconstruct situations every time we remember them and there is much room for error. But the same cognitive scientists have discovered and developed techniques that help us to reduce that error greatly. Rehearsing memories, associating new memories with salient information, and the use of memory systems greatly empower us to remember accurately and reliably.

At the same time replication, triangulation, and good research design allow scientists to certify that the results of their research actually resembles reality enough to understand and predict the workings of nature.

Nevertheless, we should be careful about "what we know". A little humility is called for because we are still once removed from the world. What we perceive will always be processed through our senses and our brain before we consciously apprehend our world. We will always be stuck with mental models of the way things work but, as long as we keep firmly in mind that they are models, that will be good enough.

No, I don't think we can know with absolute certainty what's really "out there", but we can have a consistent and reliable view of how our world works. If it's not "absolute reality", it's our world. We can't go beyond that - or can we.

The major problem is that we are incapable of directly perceiving the universe. Our sensory organs are limited and our brains are material organs that are limited in their programming to certain patterns. They are linear and time bound. Most of the universe, we can't even grasp, but we know that there are things beyond what we can grasp. What we know - our models - require other things. A physicist told me that the universe isn't made of matter - it's made of fields. We can't perceive fields, but they have to be there or else nothing we know would work.

I've had experiences that my brain can't grasp. That's part of shamanism, and there's another way we can go beyond. We're approaching a time when we can construct artificial intelligences that work qualitatively different from our material brains. They can think things that we can't. Can they open up new areas of the universe for us? I guess the question is, "Do we want them to?"



--- Websites, Blogs, and Videos...Oh my!! ---

2016

Actually, media is a new thing for me - on computers anyway. I operated the media department of the School of Pharmacy at Auburn University as a work-study student back in the 70s but computers didn't have much of a part there. I picked up an equivalent of a minor in computer science - I already had three minors, though, so I didn't get to claim it. Comparing those computers with this one (the one I'm using to compose this blog) is sorta funny.

Computer graphics back then was - okay, I won't go there.

I've even been exposed to HTML, but I can't do everything all at once. I didn't compose this blog from scratch. Fact is, I took the easy way out and used Google Blogger. But, then, I like the results - don't reinvent the wheel - right? All you have to do is just put everything together the way you want it.

I already had a website - The Therian Timeline. I took the easy way with that, too. I used Site Solution, a service provided by Yahoo Aabaco Small Business services. Again, you just tell it what you want and it does it.

I could have chosen a bunch of other such throw-it-together tools on the Internet but, you get the idea.

I'm working on a series of science demonstration videos to go along with my blogs for when I get to physics in a couple of years. See, I plan ahead! I'm a new hand to computer videos so, you guessed it - I took the easy way.

My laptop already had a video program called Dell Webcam Central, so I'm using it to put together videos, along with a desktop recorder called CamStudio Recorder. That way, I can display everything I put on the video - LibreOffice Impress presentations, spreadsheets, webcam videos, whatever - and CamStudio will record it all. So, if I can just live long enough....

Well, you'll get to see it if I do.

Until, then, I'll be here.

Stay well.
Wolf


Tuesday, July 18, 2017


--- Museums ---

2016

Museums are great places to learn.

Just visiting a museum provides opportunities to pick up things you didn't know before - not too good for picking up practical knowledge or skills. but many museums offer classes, workshops and field trips.

I've never lived in an area that didn't have museums. Most of the museums around Valley, Alabama, where I grew up, were in nearby cities like Opelika, Auburn, and Columbus, but the University at Auburn was packed with museums and I see that at least one old closed mill has been turned into a historic museum in Lanett, Alabama (It's called The Cannery).

Most university buildings maintain displays. I used to enjoy walking through the buildings at Auburn University. The art school was practically an art museum itself, hosting exhibits of the works of well known artists and showing the works of students. Between building were places like the Arboretum and the Eagle mascot's aviary.

Large towns are often cultural centers. Denver is loaded with museums and theaters. Within my hiking range is an outdoor art museum and a great paleontology museum. And then there's Dinosaur Ridge which includes two indoor museums and the side of a mountain.

I maintain a family membership with the Denver Museum of Nature and Science which provides exhibits, films, IMax, a planetarium, crowd sourced research  opportunities, classes, workshops, field trips and entertainment. In the neighborhood is also the Denver Botanic Gardens and the Denver Zoo. It's an easy train ride and hike through Capitol Hill (where the Capitol is) with great examples of architecture, from home to the museum.

I went in search of a more convenient path to the large Walmart in my area. The way I had been taking was a full day's hike. My plan was to walk through River Point shopping area to Oxford Station, catch the train to Englewood Station and walk the short distance from there to the Walmart. It was an improvement and the bonus was that my trail lead through the Denver Museum of Outdoor Arts.

This is a strange concept. There is no one building (though there is a central headquarters and indoor museum) and the museum is scattered all over Denver There is a website (http://moaonline.org/) so you can plan a visit. They even have a walking tour brochure that you can download. I went through the Englewood exhibition in December, so the central piece was a Christmas Tree.






I'll be utilizing the museums of Denver in future blogs and I'll be remembering museums of my past.


--- Intelligence ---

Being a psychologist and retired vocational evaluator, I have what may seem like an embarrassing confession to make. I don't know what "intelligence" is, but I suspect that no one else does either - at least not in any precise way. I'm certainly not sure what an IQ is good for.

I've given many IQ tests, to be sure. I generally gave them for two reasons. First, the schools required them, but I have choice words for that requirement in my reports. That anyone could use the results of a test that can be so drastically influenced by indigestion for purposes of placement - to decide the future track of a person's life - boggles my mind.

I was disillusioned by IQ tests early on. My first client, in fact, was a charming young lady who chatted with me in a witty and smart manner as I stared at her profile that assured me that her IQ was 65. That's a heck of a way to break a vocational evaluator in. After the intake interview I went straight to the behavioral specialist and said, "This can't be right." After a brief interview with the client, the behavioral specialist returned and said, "You're right. I'll retest her." And sure enough, her IQ was 65!

I never trusted an intelligence quotient again.

But I won't say I didn't like IQ tests. My favorite ones were the ones that gave multiple scores. I used them in a more straight forward fashion. Instead of trying to get a blanket score to tell me how well people could solve problems, I looked at the individual scores to see how well people could solve those specific kinds of problems, and then I compared them with scores from other tests, and more importantly, I compared all those scores with what the person had done with their life - their successes and failures, their interests and their dreams, and I pulled all that together into a narrative. No one score could have ever satisfied me when the object under my scrutiny was anything so complex and magnificent as a person.

Uh....there is one other reason I liked IQ tests. They are fun. I like puzzles, so, obviously, I liked IQ tests.

Over the next week or two, I will be reading the Yale-New Haven Teacher's Institute's unit on Human Intelligence: Theories and Developmental Origins (http://teachersinstitute.yale.edu/curriculum/units/2001/6/ last accessed 7/18/2017), and then I'll see if I can find an IQ test that I haven't taken and given so many times I already know the answers by rote (and I'll recommend some that you can take yourself). I expect to have fun.

The first section was written by Dina Pollock and focused on intrapersonal intelligence, which is one of the intelligences mentioned by Howard Gardner in his work on multiple intelligences. Intrapersonal intelligence somewhat calls into question the classical idea that personality is the characteristics that are fairly stable in a person over their lifetime. A skill  included in intrapersonal intelligence is the ability to know self and to guide the development, to some extent, of self.

Most of the exercises suggested by Ms. Pollock are group exercises, which is reasonable since the section is about knowing self and we learn much of what we know about ourselves from other people. But some of the exercises are amenable to individual self exploration. Be careful with "Know your potatoes" if you don't live alone.

I'll add to Ms. Pollock's suggestions the Berkeley Personality Inventory, available in the book "Who Do You Think You Are?" by Keith Harary (the 2nd edition was published in 2005 by Penguin Putnam Trade). This brief personality inventory allows you to explore the way you see yourself (perceived personality) and the way you would like to be (ideal personality), and you can even let others score the inventory for you to see how they perceive your personality (if you dare).

I'm working on a guide for professionals working with therians and I have just finished reviewing a report by the International Anthropomorphic Research Group (Roberts, S. E., Plante, C., Gerbasi, K., & Reysen, S. (2015). Clinical interaction with anthropomorphic phenomenon: Notes for health professionals about interacting with clients who possess this unusual identity. Health & Social Work, 40(2), e42-e50). In their study, they found that furries (people who belong to a culture based on anthropomorphic characters and/or art) use alternate personas to move from the way they see themselves closer to they way they want to be.

I would expect the players of RPGs (Role Playing Games) could use characters created during play to do the same thing. But, then, I know a lot of gamers who intentionally choose characters that are not at all like themselves and are not at all like who they would want to be, as a kind of challenge.

I suspect that RPGs present a very fertile ground for psychological and sociological research.


What is gelato?

Well, for me, it's a small adventure. I've never had gelato before but the ice cream shop down the street sells it so I figure I'd try it.

It's right next to the grocery store and I walk there three or four times a week. One of the advantages of our new house is that the block we're own is bracketed by traffic lights which allows me safe crossing every couple of minutes, which lets me get to the side with sidewalks. Down the street a ways is a patch of lavender that is helping me deal with my mild bee phobia. People in this area like their bees (I'm not so sure I would like them. There are bee collecting bags hanging off many of the trees. No, I'm sure I wouldn't like to be packed into a bag with a bunch of other bees - of course, did I mention my mild bee phobia?)

The Glacier Ice Cream and Gelato shop is a very friendly place. Since they are the ice cream specialists, I asked one of them the other day if they knew how to stop brain freeze - that agonizing headache that results from eating your ice cream too quickly. He recommended pressing my tongue to the back and roof of my mouth. That works pretty well - not perfectly but pretty well.

They have a wide selection of flavors.

My brain tries hard to make gelato have gelatin in it, but I'm told that it doesn't. Gelato just has less butterfat and more sugar than ice cream. The texture is a little different. Being from the South, the "less butterfat" thing isn't a draw for me.

Honestly, in a blind taste test, i don't thing I could tell the difference between gelato and ice cream, which does not detract from this ice cream shop - it's a great place. It just means that I'll always err on the side of a milk shake. I'm pretty crazy about milk shakes - add the butterfat.

Sunday, July 16, 2017

--- Self reference ---

"Does everything have to be about you?"

How many movies have you heard that in?

But maybe it's justified.

Here's another great experiment that you can take part in on the Online Psychology Laboratory. I won't tell you anything about it because, again, anything I told you would ruin your fun and spoil your outcome.

Find it here:

http://opl.apa.org/Experiments/CategoricalList.aspx

under social psychology, and have fun learning about yourself.



--- Crosstabulation ---

2016

Until I figured out how to create stub and banner tables with LibreOffice pivot tables, I was considering making a tabling routine for DANSYSX but that would be redundant, so my focus for crosstabulation became just the 2-way and 3-way statistics programs. Since they are involved and, from scratch, it will give me a chance to talk about my structured approach to developing Calc routines.

XTab is a big (!) matrix function, but it's a sequential function, meaning that it has many procedures that are completed before the next process is started. My first task is to outline the processes with comments. A comment in LibreOffice Basic begins with a single apostrophe. Here, I include a header:

FUNCTION XTab(optional InMat,optional InType, optional CellCont as string,
_)

(It's not complete, because I'll be adding parameters as different procedures require them.) Then, comes a descriptive comment:

'Performs a complete analysis on crosstabulation data
'from a raw data table or a crosstabulation of data.
'The input is the data and the output is a matrix containing
'a stub-and-banner table of cell data followed by
'a table of statistics. InType=1 (Crosstab), 2 (raw data)
'CellCont is a binary string designated data to be displayed in
'cells of output: 10 places: counts, exp counts, row%, column%,
'Total%, residuals, stand. residuals, adj, residuals, chi square,
'likelihood chi square, odds, compare column proportions (Bonferroni).

and a declarations and initialization section:

'Declarations and initialization. If InMat isn't specified,
'InMat=MatArray1. If InType isn't specified, InType=1 (crosstab).
'If CellCont isn't specified, CellCont="100000000000"

DIM OutMat(), Xi(), Yj(), OutR as integer, OutC as integer
DIM I AS INTEGER, J AS INTEGER, K AS INTEGER, L AS INTEGER

That will also grow as I add code. I'll declare other variables in alphabetical order, inserting each as I use them, so I can keep up with names that I've used. Double declarations and accidentally using the same variable for multiple purposes without knowing it can be major headaches.

Then, I know that I'll have to crosstabulate data if a raw data table is fed to the function, so I annotate that section:

'Crosstabulation, if needed.


Redim OutMat(), Xi(1,2), Yj(1,2)

Thinking ahead, I know I'll have to redimension the output matrix when I know what the stub-and-banner part of the display will look like. Everything below that will be pretty straightforward and I can add size as I finish each section. Each section is independent of the ones below it, so I can test each one as I finish it. I have a test data on the Report sheet of DANSYSX that I will use to progressively test each section.

The final section will just display the OutMat matrix which, by that time, will already be constructed by the procedures I have coded. OutMat is a convention I use for all macros that display a matrix.

I will have the crosstabulation saved in MatArray3, just to increase it's utility and make it compatible with the matrix functions I've programmed for DANSYS and DANSYSX.

I use a lot of spreadsheet functions in the program and that's a little tricky. The topic isn't covered in the programming guide, but you can find information online.

To use a spreadsheet function in a Basic routine, you must first declare an object variable to hold the function. You only have to declare it once since you can't use more than one spreadsheet function at a time. I use the same name, svc, and declare it as an object variable:

DIM svc AS OBJECT

Then the variable has to be set as a function variable. That only has to be done once, also.

svc=createUnoService("com.sun.star.sheet.FunctionAccess")

Uno is the object manipulation language used by LibreOffice. This statement invokes a Uno service called FunctionAccess.

After the variable is set, it can be used to call spreadsheet functions such as :

PCS=svc.CallFunction("CHIDIST",Array(CSQW,dfCS))

This statement calls the CHIDIST spreadsheet function. CHIDIST evaluates a chi square value at a specified degrees of freedom value. The CallFunction method for a function variable requires the name of the function as a string (in quotes) followed by the information to be transferred to the function in an array. The function requires a value and degrees of freedom (if you look the function up in the Calc Function Wizard, you can see the structure of the function) to be passed as an array. Even if you pass an array to a spreadsheet function, you have to use the Array statement followed by the name of the array in parentheses. You can see that here:

XMIN=SVC.CALLFUNCTION("MIN",ARRAY(Eij))

in a statement that finds the minimum value in the array Eij.

To get the return value, a variable, PCS, is set equal to the function variable.

One of the aggravating things about LibreOffice Basic is you can't return a matrix like this, which is why I went about programming a complete matrix language for DANSYS.

Displaying the results is easy. I just set the function to the output matrix, like:

XTab=OutMat.

There was some debugging that had to be done, but you can see the final product when I post DANSYSX in the Therian Timeline, here:

http://www.theriantimeline.com/excursions/labbooks

Note: DANSYSX version 1.0 is available now on the timeline.



Saturday, July 15, 2017

I figured the take away for the fourth Indiana Jones movie was, "seems we've reached an age when life stops giving you things and starts taking them away." And the implicit answer is that life can surprise you at any age.

This last relocation was an unpleasant jar but I think we might have come out better. I guess my biggest regret is that, now, we're further from the mountains than I've been since I moved to Colorado. In Broomfield, I was just far enough away that I could walk up to them close enough to be able to spit on them, but then I couldn't go any further. In South Harvey Park, I could walk a little ways into them. Now, on foot, I wouldn't even be able to get close, but, luckily, I'm learning to use the transportation resources around here and the buses and trains can get me places I never could go before.

The good thing is that there is a lot around here. My favorite restaurant is within walking distance. The University of Denver is right up the street and between here and there there is an operating observatory and a line of parks along Harvard Gulch. Also, the bus that stops in front of our house makes a beeline to City Park, which includes the zoo and the science and nature museum. I'm expecting some exciting adventures in my future.

I walked to the local library the second time today. It's an easy walk sans that killer hill above Bear Creek. The area is lushly packed with trees and the walk isn't far. The library is well stocked and, being a part of the Denver library system, cooperates with the other libraries in the area to provide a huge selection of books and other media. They also have an interesting calendar. I expect to be spending a lot of time there. Here's a picture of the Ross-University Hills branch of the Denver Public Library. The building itself is interesting - modern art.



Friday, July 14, 2017


--- A moving experience ---

I have always been interested in community affairs. Among other things I did in Selma, I was a founding member of two community action organizations, the chairperson of a social services quality assurance committee, the temporary president of a disaster relief organization, and a volunteer in several other community organizations. It's no different here in Denver.

In January of 2016, I attended a conference entitled "Talking About the Affordable Housing Crisis: Tools for Delivering Bad News." Now, that would be a funny title if the crisis were not such a disaster. At the time, there was an 80,000 affordable unit deficit and, with property values going up, land owners were evicting tenants so they could refurbish their properties and increase the rents on their houses and apartments. The evicted parties found that they had nowhere to go and whole families were on the street.

Denver has a large homeless population and many of those are evicted tenants. A report by the Denver Foundation in 2015 indicated that one out of ten respondents to a survey to study homelessness had been homeless and one in five had come close. A survey by the Metro Denver Homeless Initiative in 2014 indicated the following causes for the homelessness of respondents: Unemployment (43.9%), Housing costs too high (31.9%), Relationship break up (28.2%), Mental illness (21.3%), and Substance abuse (18.9%). (From "Homelessness in Denver: The Cold, Hard Facts Behind Six Myths" from the Westworld website: http://www.westword.com/news/homelessness-in-denver-the-cold-hard-facts-behind-six-myths-7348310 (accessed 7/13/2017)

I had made two suggestions to reduce some of the homelessness: extended families, and bus tickets to places with lower price of living, more employment, and more affordable housing. I was then an example of how well an extended family works. I was living with 8 others who I considered family, but who were unrelated. Little did I know...

Fast forward to April of 2017, some one had evidently taken a dislike to our extended family because we were informed by a zoning officer that there were too many unrelated individuals living in our house. It was a zoning law that I was not aware of, but there it was. We were being evicted. We were given a month to find a new place in which we would be in compliance with zoning laws, and move.

Digging up the resources, splitting into two households, and finding a new residence was....harrowing, but now, in July, we have settled into a new home. Make no mistake about it, though, it was close.

I believe that one of the problems, though, is that people don't know where to get help. Despite the fact that the zoning laws seem to be archaic and increase the homeless population, Denver is not one of those psychopathic places I have mentioned elsewhere. There are parts of Denver ready to help people in this kind of predicament. For instance, organizations like Colorado Housing Connects and Colorado Legal Services are zealous in helping residents with disputes with their landlords (there is a common perception that, if there is a dispute between a tenant and a land owner, Colorado will always side with the landowner and that there is no help for the tenant). And the local councilman, Kevin Flynn and his assistant Dana Montaño, were eager to help us buy the time we needed to make plans and move.

As to the move, I am no longer in the Bear Creek Valley, but I will not be changing the name of my blog, and I will still be spending considerable time along Bear Creek - after all, it's just on the other side of the South Platte River.

Thursday, July 13, 2017


--- Notes on free will ---

What is freedom? It means not being a slave to any circumstance, to any restraint, to any chance; it means compelling Fortune to enter the lists on
equal terms.

Seneca

My position on fortune, destiny, and the like is that we're dealt a hand at the beginning and we can choose how we play our cards. Destiny is at the first, not the last.

As long as free-will remained in the claws of the philosophers (and I include unthinking thinkers like Skinner in that group), humans could not reasonably be said to necessarily have free-will because their every action, every thought was predetermined by their circumstances. The physicists and neuroscientists have reopened the question. That's an odd twist.

As it is, there is too much chaos in the works to be able to say that there cannot be free will.

Freedom, in the language of physicists and statisticians, is the number of directions a thing can move in. They rarely talk about freedom - they talk about "degrees of freedom" - how many different ways can a thing move or change. I get the idea that when people talk about free will, they're talking about the ability of a person to make literally any decision within their physical capabilities. I hope not. Random action is not free will. Free will is the ability to choose between any of a range of rational decisions available to a person. It's the ability to think things out and then decide a course of actions.

Cognition is fuzzy enough to easily allow for free choice. The logical positivism set (may logical positivism rest in peace) wanted to believe that everything was strictly determined - it would make the calculation of everything a lot easier. But then Heisenberg came along with his uncertainty nonsense and put a stop to their dreaming.

I often have recourse to, "That's a good explanation, but not a very good excuse." Serial and mass murderers often have very good reasons for doing what they do. Hate humanity? I can see that. I was mistreated as a child? Yep, so people go through that. Humans taste good? Probably do.

But those are not good excuses. I know too  many people who had horrible childhoods and they grew up to be caring, helpful people. I know too many people who have every reason to hate humanity but, surprisingly, don't. I don't want to hear anyone use their bad temper as an excuse. I have a murderous temper, I can easily go into a berserker's rage, but I don't. People are supposed to develop self-control as a part of growing up. I don't want to hear any one say, "I just couldn't control my sex drive." I'm a satyr and have been plagued with a raging libido since I was 8 years old. If anyone had the right to claim over-active sex drive it would be me, yet I respect other people. Too many minorities grew up to be great people who quite obviously love humanity and live lives that make life worth living for others for other individuals to justify bad actions by, "I was mistreated by The Man."

So I just can't buy the, "I had to do it," or "the devil made me do it," or the "they just caught me in a weak moment," defense. I believe that people do have choice and they're responsible personally for how they use it.



Wednesday, July 12, 2017


--- Me and sociology ---

I've just been through a harrowing sociological adventure (but an adventure, nevertheless) but, being a sociologist, I can easily see the interesting side. See? Sociology does have it's uses. But, before I blog about it, I should cover my own history in sociology.

By the way, it is my intention to maintain a nonadversarial position in these blogs regardless of how painful a topic is. I don't want to slip into blaming. After all, and I will state my position here are honestly as I know how, people are never the problem. Things, situations, faulty procedures are problems. It's not that I believe that there are no bad people. I most certainly do believe that there are bad people, but the solution is to nullify the action of those people.

Also note that I'm slipping into next year's topics, here.

I have always been disturbed by "problem solving" sessions that turn out to be finger-pointing sessions. Bedrock problems are always things and finger pointing always distracts from addressing the real problems.

When I entered Auburn University in the early 70s, my intention was to enter a profession that would allow me to help people who had lost their ability to function get their lives back. I hit on a double major in pharmacy and psychology. I didn't want to be a doctor (nor did I want to take the huge chunk out of my life that it would require) but I did want a strong physiological background. I wanted to understand what I would be working with - the human brain. Pharmacy seemed to be the quickest path between where I was and where I wanted to be. I consider it to have been a good choice even though a series of grueling pharmaceutical chemistry courses caused me to drop out of the pharmacy curriculum a couple of quarters early and transfer to the School of Psychology as a single major.

In the 70s, studying psychology meant that I had to claim a particular school of thought to focus on. There were three such directions at Auburn - behaviorism (which was like a virus at the time pervading everything. Noam Chomsky should be canonized.), personality psychology, and social and industrial psychology. I went the latter route, and I also minored in sociology. Again, I don't regret the choice.

After graduation, I took a very enforced hiatus. My autoimmune problems settled in my knees and put me out of operation from April (the problem flared up on Easter Sunday), to November, when I received a letter from Auburn inviting me to apply for their Rehabilitation and Special Education graduate school. This opened a dream opportunity for me. I hated the you-have-to-choose-a-school mentality in psychology and there was no integrative psychology options available back then. Rehabilitation was as close to integrative psychology as there was. A years work placed me into the profession I had been looking for. I worked with individuals as a rehabilitation specialist and professionalism required that I serve my community, so I had plenty of opportunity to help build and develop communities.

So, if I'm a sociologist, do I have any distinctive characteristics as a sociologist that sets me apart from other sociologists and that I should warn you about?

Well, sociologists like me look at cities, towns, families, churches, etc. as individuals in their own right. Cities, for instance, have bodies, minds, souls, spirits as much as individual humans do. They can be healthy, be hurt, be sick, be happy or sad, just like any individual. Cities have egos, subconsciouses, administrative functions. They can be mentally ill and they can sometimes be scary people. They can be nurturing, caring people.

Also, I'm a shaman, which brings the group and the individual more solidly together. Whereas doctors treat individuals, and psychologists sometimes treat small groups, shamans are concerned about both their communities and the people that make them up.

So, how can cities, the groups that make them up, and the people that live there all be individuals? Simple answer: we're colonial animals - like volvox. All the cells in a volvox are individual animals. The volvox itself is also an individual animal.


Tuesday, July 11, 2017


--- Graphics ---

2016

One of my more frustrating programming episodes occurred with one of OpenOffices updates. I tried to install the new version and it wouldn't install. The suggestion on the user forum was to uninstall the older version and then try to install the new version. The new install still didn't work so I had no working version of OpenOffice and a lot of work on DANSYS that I had not saved. Dumb of me - irritating of OpenOffice. That was when I decided to junk OpenOffice and switch to LibreOffice.

The biggest loss was a set of programs I had put a lot of work into that expanded the ability of OpenOffice Calc to produce statistical graphs. They constituted a graph building suite. Instead of creating a set of predesigned graphs (as with the charts currently produced by Calc), I could build graphs from elements such as axes, points, lines, and such. I decided to ditch the idea. I just didn't have the heart to redo all that work, and I had other programs that would produce all the statistical graphs I would ever need.

But then I started thinking (often a big mistake, but one that often leads to adventure) that it would be nice to have DANSYSX be able to generate some simple graphics. Points and lines and such are useful for a lot more than making statistical graphs.

One limitation of spreadsheet functions is that they can only output data into cells, so I couldn't use functions to generate geometrical objects on a spreadsheet. I would have to use subroutines, and thus commands, to do the job, and that meant creating a menu of graphical commands.

I also wanted to be able to translate the positions of the geometrical objects into a specified area - a frame. I had done that with the OpenOffice commands, so I should be able to do it again for LibreOffice.

This is a different kind of programming than creating a function to output a value or matrix of values. I had to actually place objects on a spreadsheet. That meant that I had to have the program determine where everything had to go and place it there. LibreOffice Basic can do that and I'll show you the frame routine. You can find the others in the Graphics folder of the DANSYSX macros.

One thing I remember doing over and over in the older programs was translating data points from one range (the range of data values) to another (the range of positions on a frame on a spreadsheet). That got old, so, this time I created a very simple function to do that. It looks like this:

Function Rescale(c,a,b,y,z)
'Rescales a value, c, between a and b to be proportionally
'between y and z)
Rescale=(c-a)*(z-y)/(b-a)+y

End Function

That was easy and, if you went through the article, DANSYSX: Complex cubic equations, you know what that all means.

The way I had the older macros work is that the user would create a specification form on a spreadsheet for a specific object, fill it out, process the specifications, and then display the object on the spreadsheet.  I've trimmed this four step process down in DANSYSX except for the frame. You still have to process the specifications for the frame. A frame has to be generated (whether it's displayed or not) so LibreOffice knows where to place the graphics object.

The Specification routine is tedious (there's a lot of information required to specify the frame) but straightforward. A frame is just a rectangle. Draw a rectangle and you have a frame that you can situate everything else on.

The only complication is that I need different specifications according to the kind of background to have on the frame, so I had to work up a dialog to select the kind of background before printing the specification form.

Here is the specification routine:

SUB FrameSpec()
'Prints out a frame specification form onto a spreadsheet
'beginning at the current cell.
DIM I AS INTEGER, J AS INTEGER
DIM RP, CP, DLG AS OBJECT, STL AS INTEGER, LBX AS OBJECT
DIM oDoc AS OBJECT, oSheet AS OBJECT, oSel AS OBJECT
DIM addr AS OBJECT, M AS INTEGER, N AS INTEGER
DIM oCell AS OBJECT, oView AS OBJECT



'Display dialog Frame

DialogLibraries.LoadLibrary("Standard")
DLG=CreateUnoDialog(DialogLibraries.Standard.Frame)
DLG.Execute()


LBX=DLG.getControl("ListBox1")
STL=LBX.SelectedItemPos

IF DLG.Execute()=0 THEN EXIT SUB


'Print form
 oDoc=ThisComponent
 oView=ThisComponent.getCurrentController()
 oSheet=oView.getActiveSheet()
 oSel=oDoc.getCurrentSelection
 addr=oSel.getRangeAddress()

 m=addr.StartRow
 n=addr.StartColumn

oCell=oSheet.getCellByPosition(n,m)
oCell.String="Frame"
oCell=oSheet.getCellByPosition(n,m+1)
oCell.String="Position 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+2)
oCell.String="X"
oCell=oSheet.getCellByPosition(n+1,m+3)
oCell.String="Y"
oCell=oSheet.getCellByPosition(n,m+4)
oCell.String="Size 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+5)
oCell.String="Width"
oCell=oSheet.getCellByPosition(n+1,m+6)
oCell.String="Height"
oCell=oSheet.getCellByPosition(n,m+7)
oCell.String="Margins: 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+8)
oCell.String="Top"
oCell=oSheet.getCellByPosition(n+1,m+9)
oCell.String="Bottom"
oCell=oSheet.getCellByPosition(n+1,m+10)
oCell.String="Left"
oCell=oSheet.getCellByPosition(n+1,m+11)
oCell.String="Right"
oCell=oSheet.getCellByPosition(n,m+12)
oCell.String="Caption"
oCell=oSheet.getCellByPosition(n+1,m+13)
oCell.String="Top"
oCell=oSheet.getCellByPosition(n+1,m+14)
oCell.String="Bottom"
oCell=oSheet.getCellByPosition(n+1,m+15)
oCell.String="Left"
oCell=oSheet.getCellByPosition(n+1,m+16)
oCell.String="Right"
oCell=oSheet.getCellByPosition(n,m+17)
oCell.String="Transparency: percent"
oCell=oSheet.getCellByPosition(n,m+18)
oCell.String="Border"
oCell=oSheet.getCellByPosition(n+1,m+19)
oCell.String="Line Style: NONE, SOLID, DASH"
oCell=oSheet.getCellByPosition(n+1,m+20)
oCell.String="Line Color: RGB"
oCell=oSheet.getCellByPosition(n+1,m+21)
oCell.String="Line Transparency :percent"
oCell=oSheet.getCellByPosition(n+1,m+22)
oCell.String="Line Width: 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+23)
oCell.String="Line Joint: NONE, MIDDLE, BEVEL, MITER, ROUND"
SELECT CASE STL
CASE=0
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Background: NONE"
CASE=1
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Background: SOLID"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="Red"
oCell=oSheet.getCellByPosition(n+1,m+26)
oCell.String="Green"
oCell=oSheet.getCellByPosition(n+1,m+27)
oCell.String="Blue"
CASE=2
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Background: HATCH"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="Style: SINGLE, DOUBLE, TRIPLE"
oCell=oSheet.getCellByPosition(n+1,m+26)
oCell.String="Color: RGB"
oCell=oSheet.getCellByPosition(n+1,m+27)
oCell.String="Distance: 1/100 mm"
oCell=oSheet.getCellByPosition(n+1,m+28)
oCell.String="Angle: 1/10 degree"
CASE=3
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Bitmap - Named"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="Name"
oCell=oSheet.getCellByPosition(n+1,m+26)
oCell.String="Style: REPEAT, STRETCH, NO_REPEAT"
CASE=4
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Bitmap - URL"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="URL"
CASE=5
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Gradient - Named"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="Name"
CASE=6
oCell=oSheet.getCellByPosition(n,m+24)
oCell.String="Gradient - Custom"
oCell=oSheet.getCellByPosition(n+1,m+25)
oCell.String="Style: LINEAR, AXIAL, RADIAL, ELLIPTICAL, SQUARE, RECT"
oCell=oSheet.getCellByPosition(n+1,m+26)
oCell.String="Start Color: RGB"
oCell=oSheet.getCellByPosition(n+1,m+27)
oCell.String="End Color: RGB"
oCell=oSheet.getCellByPosition(n+1,m+28)
oCell.String="Angle: 1/10 degree"
oCell=oSheet.getCellByPosition(n+1,m+29)
oCell.String="X Offset: 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+30)
oCell.String="Y Offset: 1/10 mm"
oCell=oSheet.getCellByPosition(n+1,m+31)
oCell.String="Start Intensity: percent"
oCell=oSheet.getCellByPosition(n+1,m+32)
oCell.String="End Intensity: percent"
oCell=oSheet.getCellByPosition(n+1,m+33)
oCell.String="Step Count: number of color graduations"
END SELECT

It's long but fairly simple. It does illustrate how you can get things from a program onto a spreadsheet, though. Let's look at the sections.

Variables can be set to hold things like documents, sheets, cells, ranges, shapes, and such but they have to be declared as objects. This program uses several object variables.

DialogLibraries.LoadLibrary("Standard")
DLG=CreateUnoDialog(DialogLibraries.Standard.Frame)
DLG.Execute()


LBX=DLG.getControl("ListBox1")
STL=LBX.SelectedItemPos

IF DLG.Execute()=0 THEN EXIT SUB

This section displays the dialog that has the list box of styles for backgrounds. I constructed the dialog in the dialog editor (Select Macro>Organizer>Dialogs tab) with a listbox (I specified the items in the listbox in the properties settings for the listbox.), a Okay button, and a Cancel button. Here's what the dialog editor looked like:



"DialogLibraries.LoadLibrary("Standard")" loads the library that contains the Frame dialog.
"DLG=CreateUnoDialog(DialogLibraries.Standard.Frame)" sets the DLG object variable to contain the Frame dialog.
"DLG.Execute()" displays the dialog. As long as the dialog is displayed and working, nothing else runs in Calc.
"LBX=DLG.getControl("ListBox1")" loads the listbox1 control into object variable LBX and "STL=LBX.SelectedItemPos" gets the selected text string from the listbox1.
"IF DLG.Execute()=0 THEN EXIT SUB" specifies that, if the Cancel button is clicked (which generates a 0 from the DLG object), that the subroutine is closed without doing anything.

 oDoc=ThisComponent
 oView=ThisComponent.getCurrentController()
 oSheet=oView.getActiveSheet()
 oSel=oDoc.getCurrentSelection
 addr=oSel.getRangeAddress()

 m=addr.StartRow
 n=addr.StartColumn

The oDoc variable is set to contain the document that is currently open (DANSYSX)
The oView variable is set to contain the current documents view.
The oSheet variable is set to contain the currently active sheet in the view of the document.
The oSel variable is set to contain the currently active range (in this case, just one cell, the top left cell intended to start the Frame form.)
The addr variable contains the address structure of oSel. It contains the starting and ending cell row and columns as a data structure.

Data structures are addressed by the name of the data structure (in this case addr) followed by a period and the name of the specific value to be accessed. For instance, addr.StartRow returns the value of the StartRow value in addr. The integer variable m is set to the StartRow value and the integer variable n is set to the StartColumn value.

The rest of the subroutine is just a long list of statement of the form:

oCell=oSheet.getCellByPosition(n,m)
oCell.String="Frame"

Here, the oCell variable is set to the cell at column n and row m on the currently active sheet. Notice that cell positions are opposite to matrix positions. Matrix positions are addressed by (row,column); cell positions are addresses by (column, row) order. This is in keeping with the way cells are addressed on spreadsheets. A cell at position C5 is at column C and row 5. It's also not obvious here, but the first row on a spreadsheet is row 0, and the first column is column 0.

I use a Select Case structure to decide the labels to output onto the spreadsheet according to which background style is chosen. The Select Case structure works like this:

SELECT CASE (statement)
CASE (value)
CASE (value)
...
CASE (value)
END SELECT

The statement in the first line specifies what the following case values refer to. In this example, the case is the value of STL, the numerical position of the selected style in the dropdown list of the dialog. The first CASE statement is CASE 0, which is the value of the first item, NONE, in the list. If STL=0, the block of statements following CASE 0 is executed and, then, the other case statements are ignored. END SELECT marks the end of the Select Case structure. Notice that the Case values can be numerical or string values, inequalities like >5 or <=3, or statements like "between 2 and 5".

When this routine is run from the Graphics menu>FrameSpec, a form is printed onto the active spreadsheet beginning at the selected cell. This form can be filled in to specify a frame for other graphic objects.

Saving all this information so that it doesn't go away between sessions requires that I construct a hidden spreadsheet to save it on. You can see that by unhiding the Graphics sheet in DANSYSX. Right click on any sheet tab and select the Show Sheet... command. It will open a list of hidden sheet. Double click Graphics. Be careful and don't change anything. You'll the information generated by the FrameSpec command in the top part of the sheet. Below that, though, are several calculated value. These are values necessary to scale values to the frame, taking margin specifications into account.

The FrameGen command transfers information from the frame table to the FrameSpec sheet. It uses much of the same Basic and Uno commands but there are two sheets to deal with, the sheet the table is on and the FrameSpec sheet. You will recognize most of the code if you look at it.

This pattern is repeated a lot.

oCell=oSheet.getCellByPosition(n1+1,m1+0)
aCell=aSheet.getCellByPosition(1,0)
aCell.Value=oCell.Value

That, of course, transfers the information from the first column to the right and top row of the frame table to the first column and top row of the Frame Spec sheet. These lines are quite simple but tedious to write. Luckily, there is a lot of cut, paste, and modification going on.

There is an interesting little subroutine tagged onto the end that parses out a string of color specifications. Here, color is specified by three numbers from 0 to 225 specifying how much red, green, or blue is used to create the color desired. The subroutine is called using the statement:

GOSUB FG10

The cell containing the string is first specified as aCell. The subroutine has to come after the last line of the main subroutine:

EXIT SUB

and it must begin with the calling label. Here is the subroutine:

FG10: 'RGB subroutine. Sets R, G, and B.
RGBSTR=aCell.String
Clr=""
S=1
FOR I=1 TO LEN(RGBStr)
Clr=Clr & MID(RGBStr,I,1)
IF MID(RGBStr,I,1)="," OR I=LEN(RGBStr) THEN
IF S=1 THEN
R=Val(Clr)
ELSEIF S=2 THEN
G=Val(Clr)
ELSE
B=Val(Clr)
END IF
S=S+1
Clr=""
END IF
NEXT I

RETURN


 END SUB

The string in aCell is transferred into the RGBStr variable and the following loop structure takes the string apart, converting the parts between the commas into numerical values stored in the R, G, and B variables. The last line of the subroutine is RETURN, which switches processing back to the line immediately following the last GOSUB statement. Finally, the whole macro is closed out using the END SUB statement.

More than one subroutine with different line labels can be placed at the end of a macro, but they all have to come before the END SUB or END FUNCTION statement.

The FrameShow command acually places the graphics frame on the spreadsheet. It is just a drawn rectangle and the LibreOffice Basic Programmer's Guide very adequately explains how to code for various drawn shapes and I will direct you to that since there are a lot of possibilities, but I will point out a few things that it doesn't make terribly clear.

First, every spreadsheet has it's own drawing page. In Draw and Impress, this is pretty explicit, but not for Calc. The way the drawing page for the active sheet is specified is:

oSheet = aDoc.getcurrentcontroller.activesheet
DPAGE=oSheet.DrawPage

The first line defines the active sheet and loads it into an object variable (oSheet). The second loads the drawing page of oSheet into another object variable called DPAGE. Later, the frame, which is a rectangle stored in the object variable oRect, is placed on the drawing page by the following line:

DPAGE.add(oRect)

The frame is built up by sequentially adding the frame and all the specified captions (which are rectangles with text) with similar commands.

Other commands to create points, lines, ellipses, and boxes will be very similar. They will all be in the Graphics folder of the DANSYSX macro library.


Sunday, July 9, 2017


--- Notes on reason ---

Shake off all the fears of servile prejudices, under which weak minds are servilely crouched. Fix reason firmly in her seat, and call on her tribunal for every fact, every opinion. Question with boldness even the existence of God; because, if there be one, he must more approve of the homage of reason than
that of blindfolded fear.

Thomas Jefferson

Mr. President had a lot of trouble from the church in the early United States. He thought that they had way too much power. They didn't think that they wanted to lose any of that power. And so it goes...

As it stands, I agree with Mr. Jefferson inasmuch as God doesn't mind people questioning him. Jesus didn't bust Thomas' chops when Thomas doubted him, and the Bible includes this very passage: "Come now, let us reason together" (Isaiah 1:18).

I am a Christian and I have experiential reasons to be. I've always held that I cannot expect people to believe things they have no reason to believe and I realize that others experiences are not the same as mine.

And what I'm saying at the bedrock level is that experience comes first, then reason, and then belief - that in all things.

A person cannot be expected to accept things they have not experienced, even if the only experience is reading something in a book and saying, "That makes sense."

But experience isn't foolproof. A person can perceive something and say, "I'm imagining that." That's why reason must come next.

Belief is firmly bound with responsibility. A person can choose to believe or disbelieve anything. The powers of human beings to believe what they want is truly awe-inspiring. But then they will have to deal with the consequences of what they have believed. There is no choice in that.


Thursday, July 6, 2017


--- DANSYSX: Complex cubic equations ---

2016

Both LibreOffice Calc and DANSYS are able to do basic complex arithmetic, but I want DANSYSX to be able to do advanced complex mathematics. There are a number of algorithms on Jean Pierre Moreau's excellent website (http://jean-pierre.moreau.pagesperso-orange.fr/index.html) that I want to adapt to this purpose.

The first is an algorithm to determine the roots of a cubic equation with complex coefficients. It uses the cubic formula (like the quadratic formula, but for cubic functions):

z(1...3) = cube root (-q/2 + square root(q^2/4 + p^3/27)) + cube root(-q/2 - square root(q^2/4 + p^3/27))

where: p = (c/a) - (b^2/3a^2)
           q = (2b^3/27a^3) + (d/a) - (bc)/3a^2)

I want the complex mathematics programs to be in their own folder so I use Tools>Macros>Organize Macros>LibreOffice Basic>Organizer to create the new folder, Complex under DANSYSX.ods>Standard. To begin programming, I open the LibreOffice Basic Macros dialog and select DANSYSX.ods>Standard>Complex and click the Edit button.

I am more comfortable programming in an environment where indexes run from 1 to whatever (instead of beginning at 0), so I add the Option Base 1 to the top of the editor Window, and change the name of the blank macro from Main to CompCube. I will also want the Macro to be a function so I change the header from Sub to Function and the result looks like this:

REM  *****  BASIC  *****

Option Base 1
Function CompCube

End Function

Now, everything is ready for me to start programming. I have to decide what I want my program to look like - the inputs and outputs. And I want to add a descriptive comment to the top of the code.

Looking at the Moreau algorithm, I see that I will need to input four complex numbers and I want to stick to the LibreOffice convention of designating complex numbers as strings of the form a±bi, so I want to pass four strings to the function. I will also want to output three complex numbers because a cubic equation will have up to three unique roots. So I'll start with the following header:

REM  *****  BASIC  *****

Option Base 1
Function CompCube(Z1 AS STRING, Z2 AS STRING, Z3 AS STRING, Z4 AS STRING)
'The following code determines the roots of a cubic equation
'defined by four complex coefficients:
'a z^3 + b z^2 + c z + d = 0
'The algorithm is an adaptation of one (CRoot3) from the Jean Pierre Moreau
'website: http://jean-pierre.moreau.pagesperso-orange.fr/Basic/croot3_bas.txt
'Several subroutines are used to perform elementary complex
'calculations and are shared with other DANSYSX programs.
'The global variables CMPN 1, 2, ..., 6 are also used to
'transfer complex variables between subroutines.

End Function

Most of the code will be in upper caps. This is not required by LibreOffice Basic, but I have found that I catch errors a lot quicker if everything is capitalized.

The Function statement names the program CompCube. The "Comp" part is a convention I established in DANSYS. Functions that deal with complex numbers begin with "Comp". The variables passed to the function (also called "parameters" or "arguments") are defined in the parentheses following the function name.

One of the flaws in LibreOffice Basic (all programming languages have weak spots) is that you can't transfer matrix values to and from functions and subroutines. (When I say "can't", I mean that I looked in all the resources I could find and tried to figure it out and couldn't - there may be a way but it remains a mystery to me.) I will have to transfer two value matrices (representing complex numbers), so I will store each value in a 1x2 array called CMPN1, CMPN2, to CMPN6. These arrays are defined as global variables (variables that can be used by all routines in DANSYSX and that won't lose their values between calls) which as designated in the Structures folder as, for instance:

Global CMPN1(1,2)

This generates a global array that has 1 row and 2 columns.

Global variables have to be defined outside of any function or subroutine. I collect them in the Structures folder.

I will also have to output three strings representing complex numbers so, to call this function from a spreadsheet, the user will select a 3x1 range of cells and type "=CompCube()' Inside the parentheses will either be four strings or the addresses of four cells containing strings.

I have written programs before that take complex numbers and have saved the code to do it in a KeyNote file of other handy code snippets that I use over and over. I will do that in the initialization section of the program.

Now, I go through the original code and figure out all the variables that I will need and declare them. At first guess, I come up with:

DIM A(2), B(2), C(2), D(2), U(2), V(2), W(2)
DIM DET(2), P(2), Q(2), SDET(2), U1(2), U2(2)
DIM V1(2), V2(2), ZT1(2), ZT2(2), Z(9,2)

The DIM statement is simple. It is just DIM followed by a list of variables separated by commas.

All the variables but the last are arrays with two places. Those are complex numbers.

Using my tried and true complex number decoders in the initialization section to load A, B, C, and D from Z1, Z2, Z3, and Z4, I wrote:

DIM A(2), B(2), C(2), D(2), U(2), V(2), W(2)
DIM DET(2), P(2), Q(2), SDET(2), U1(2), U2(2)
DIM V1(2), V2(2) ZT1(2), ZT2(2), Z(9,2)
DIM I AS INTEGER, ST AS STRING
DIM OutMat(3,1) AS STRING

OutMat is a convention I use to output arrays. I load all the data I want to display into an array and I set the function equal to that array. In this case, I'll be displaying three complex number strings - 3 rows and 1 column.

Notice that the last two variables are explicitly specified to be integers. LibreOffice allows you to specify variables as INTEGER (between values of -32768 and 32767), LONG (integers between -2147483648 and 2147483647), SINGLE (floating-point values between 3.402823E38 and 1.401298R-45), DOUBLE (floating-point values between 1.79769313486232E308 and 4.94065645841247E-324), CURRENCY (64 bit numbers displayed as fixed decimal numbers with 15 non-whole and 4 decimal places ranging from -922337203685477.5808 to 922337203685477.5807), STRING (character strings holding up to 65,535 characters), BOOLEAN (stores TRUE/1 or FALSE/0), and DATE (date and time values stores in an internal format.) Constants can also be declared using the keyword CONST (instead of DIM).

Variable types can also be specified by tagging a declaration symbol to the end of a variable name as follows:

Single !
Double #
Currency @
String $
For instance, DIM Money@ would declare the variable Money as the type Currency.

ST=""
FOR I=1 TO LEN(Z1)
IF MID(Z1,I,1)="+" OR MID(Z1,I,1)="-" THEN
A(1)=VAL(ST)
ST=""
END IF
ST=ST & MID(Z1,I,1)
NEXT I
A(2)=VAL(ST)

ST=""
FOR I=1 TO LEN(Z2)
IF MID(Z2,I,1)="+" OR MID(Z2,I,1)="-" THEN
B(1)=VAL(ST)
ST=""
END IF
ST=ST & MID(Z2,I,1)
NEXT I
B(2)=VAL(ST)

ST=""
FOR I=1 TO LEN(Z3)
IF MID(Z3,I,1)="+" OR MID(Z3,I,1)="-" THEN
C(1)=VAL(ST)
ST=""
END IF
ST=ST & MID(Z3,I,1)
NEXT I
C(2)=VAL(ST)

ST=""
FOR I=1 TO LEN(Z4)
IF MID(Z4,I,1)="+" OR MID(Z4,I,1)="-" THEN
D(1)=VAL(ST)
ST=""
END IF
ST=ST & MID(Z4,I,1)
NEXT I
D(2)=VAL(ST)

MSGBOX A(1)
MSGBOX A(2)
MSGBOX B(1)
MSGBOX B(2)
MSGBOX C(1)
MSGBOX C(2)
MSGBOX D(1)
MSGBOX D(2)

Each block of code beginning with ST works like this: First a blank string is generated in the string variable ST. Then, the following loop looks at each character of an input string Z. If the character is a + or a -, the first cell of a two place array is loaded with the numerical value of whatever is in ST and ST is emptied. If the character is anything else, it is tagged onto the right end of the string in ST and the next character comes up for review.  This goes on until each character of the input string (LEN(Z) is the number of characters in string Z) has been considered, then the numerical value of whatever is left in ST is loaded into the second cell of the two place variable.

The MID(Z,I,1) extracts 1 character from the ith position of string Z.

MSGBOX is a command that causes the following to be displayed in a message box. In this case, I use it to make sure that the input values are being transferred to the complex value arrays. They work so I'm happy. The MSGBOX commands will be deleted.

The next step requires that I add a subroutine that performs complex division and one that performs complex multiplication. I design these so that they work on the complex numbers stored in CMPN1 and CMPN2 and return an answer in CMPN3. You notice that the CMPN global variables act like a stack of registers for complex numbers. Here are CDIV and CMUL:

SUB CDIV()
'Subroutine to divide two complex numbers.
DIM CC(2), D AS DOUBLE, Z(2)

'Determine the value of the denominator
D=CMPN2(1,1)*CMPN2(1,1)+CMPN2(1,2)*CMPN2(1,2)
IF D<1e-15 THEN
MSGBOX "Complex divisiion by 0"
EXIT SUB
ELSE
CC(1)=CMPN2(1,1)
CC(2)=-CMPN2(1,2)
CMPN4(1,1)=CMPN1(1,1)
CMPN4(1,2)=CMPN1(1,2)
CMPN5(1,1)=CMPN2(1,1)
CMPN5(1,2)=CMPN2(1,2)
CMPN2(1,1)=CC(1)
CMPN2(1,2)=CC(2)
CMUL
Z(1)=CMPN3(1,1)
Z(2)=CMPN3(1,2)
CMPN2(1,1)=CMPN5(1,1)
CMPN2(1,2)=CMPN5(1,2)
CMPN3(1,1)=CMPN3(1,1)/D
CMPN3(1,2)=CMPN3(1,2)/D
END IF
END SUB

SUB CMUL()
'DETERMINE THE PRODUCT OF CMPN1 AND CMPN2
'SAVE THE RESULT IN CMPN3
CMPN3(1,1)=CMPN1(1,1)*CMPN2(1,1)-CMPN1(1,2)*CMPN2(1,2)
CMPN3(1,2)=CMPN1(1,1)*CMPN2(1,2)+CMPN1(1,2)*CMPN2(1,1)
END SUB

You will recognize most of the arithmetic operators. * is LibreOffice's multiplication operator (instead of x) and an exponent is denoted by the symbol ^. For instance, x squared would look like: x^2.

The difference between a function and a sub (or subroutine) is that the function returns a value and a sub does something but does not output a value. For instance, CMUL calculates the product of two complex numbers and loads them into a global array, but does not return the product to the program that called the sub. That program has to get the value from the global array. Here's the part of CompCube that uses CDIV and retrieves the result:

CMPN1(1,1)=C(1)
CMPN1(1,2)=C(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CDIV
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)
MSGBOX U(1)
MSGBOX U(2)

This divides the complex number in C by the one in A. In order for CDIV to work C has to be loaded into CMPN1 and A has to be loaded into CMPN2. To call CDIV, I simply type CDIV. Then to get the result back as U, I load CMPN3 into U. Again, the MSGBOX commands display the result and, after I'm satisfied that CDIV works, I'll delete them. That's called "debugging". I prefer to debug as I go.

Now, to complete the calculation of P:

CMPN1(1,1)=B(1)
CMPN1(1,2)=B(2)
CMPN2(1,1)=B(1)
CMPN2(1,2)=B(2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

CMPN1(1,1)=A(1)
CMPN1(1,2)=A(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CMUL
W(1)=CMPN3(1,1)
W(2)=CMPN3(1,2)

W(1)=3*W(1)
W(2)=3*W(2)

CMPN1(1,1)=V(1)
CMPN1(1,2)=V(2)
CMPN2(1,1)=W(1)
CMPN2(1,2)=W(2)
CDIV
ZT1(1)=CMPN3(1,1)
ZT1(2)=CMPN3(1,2)
P(1)=U(1)-ZT1(1)
P(2)=U(2)-ZT1(2)

You should be able to follow that. It's the same process I used to calculate c/a. It checks out, so I can continue.

The next piece of the puzzle is Q. It's a straight forward but long calculation (you see why they just teach the quadratic formula in high school and leave the cubic and quartic monsters for folks that are too curious for their own good.) That section of code looks like:

'Calculate q=2b^3/27a^3 + d/a - bc/3a^2
CMPN1(1,1)=B(1)
CMPN1(1,2)=B(2)
CMPN2(1,1)=V(1)
CMPN2(1,2)=V(2)
CMUL
W(1)=CMPN3(1,1)
W(2)=CMPN3(1,2)

W(1)=2*W(1)
W(2)=2*W(2)

CMPN1(1,1)=A(1)
CMPN1(1,2)=A(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

CMPN1(1,1)=U(1)
CMPN1(1,2)=U(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

V(1)=27*V(1)
V(2)=27*V(2)

CMPN1(1,1)=W(1)
CMPN1(1,2)=W(2)
CMPN2(1,1)=V(1)
CMPN2(1,2)=V(2)
CDIV
Q(1)=CMPN3(1,1)
Q(2)=CMPN3(1,2)

CMPN1(1,1)=D(1)
CMPN1(1,2)=D(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CDIV
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

Q(1)=Q(1)+U(1)
Q(2)=Q(2)+U(2)

CMPN1(1,1)=B(1)
CMPN1(1,2)=B(2)
CMPN2(1,1)=C(1)
CMPN2(1,2)=C(2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

CMPN1(1,1)=A(1)
CMPN1(1,2)=A(2)
CMPN2(1,1)=A(1)
CMPN2(1,2)=A(2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

V(1)=3*V(1)
V(2)=3*V(2)

CMPN1(1,1)=U(1)
CMPN1(1,2)=U(2)
CMPN2(1,1)=V(1)
CMPN2(1,2)=V(2)
CDIV
W(1)=CMPN3(1,1)
W(2)=CMPN3(1,2)

Q(1)=Q(1)-W(1)
Q(2)=Q(2)-W(2)

It's very repetitive and allows me to copy, paste,and modify most of it from earlier code. Then there is det:


'Calculate DET = q^2/4 + p^3/27

CMPN1(1,1)=Q(1)
CMPN1(1,2)=Q(2)
CMPN2(1,1)=Q(1)
CMPN2(1,2)=Q(2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)
U(1)=U(1)/4
U(2)=U(2)/4

CMPN1(1,1)=P(1)
CMPN1(1,2)=P(2)
CMPN2(1,1)=P(1)
CMPN2(1,2)=P(2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

CMPN1(1,1)=P(1)
CMPN1(1,2)=P(2)
CMPN2(1,1)=V(1)
CMPN2(1,2)=V(2)
CMUL
W(1)=CMPN3(1,1)
W(2)=CMPN3(1,2)
W(1)=W(1)/27
W(2)=W(2)/27

DET(1)=U(1)+W(1)
DET(2)=U(2)+W(2)

Now, I need the square root of DET, and for that, I need the following subroutine that calculates the square root of the complex number stored in CMPN1.

SUB CSQRT()
'Determine the square root of CMPN1
'Save the result in CMPN3.
DIM R AS DOUBLE
R=SQR(CMPN1(1,1)*CMPN1(1,1)+CMPN1(1,2)*CMPN1(1,2))
CMPN3(1,1)=SQR((R+CMPN1(1,1))/2)
CMPN3(1,2)=SQR((R-CMPN1(1,1))/2)
IF CMPN1(1,2)<0 THEN CMPN3(1,2)=-CMPN3(1,2)
END SUB

The result is stored in CMPN3. SQR is the LibreOffice command to take the square root of the value in parentheses following it.

Now, the section of CompCube that calculates the square root of DET is:

'Calculate the square root of DET

CMPN1(1,1)=DET(1)
CMPN2(1,2)=DET(2)
CSQRT
SDET(1)=CMPN3(1,1)
SDET(2)=CMPN3(1,2)

V(1)=-Q(1)/2+SDET(1)
V(2)=-Q(2)/2+SDET(2)

If you look back up at the cubic formula, you will see where those last two statements came from.

So, I need the three cube roots of V and to get that, I need a subroutine to calculate the cube root of CMPN1 and store the results in CMPN2, CMPN3, and CMPN4, and that subroutine will need a function to determine the phase of CMPN1.

Here's the function CPHASE:

FUNCTION CPHASE(NUM,DENOM)
'Returns the phase of a complex number between -pi and pi
DIM PPI AS DOUBLE, CP AS DOUBLE
PPI=4*ATN(1)
IF ABS(DENOM)<1e-15 THEN
IF NUM<0 THEN
CPHASE=-PP1/2
ELSE
CPHASE=PPI/2
END IF
ELSE
CP=ATN(NUM/DENOM)
IF DENOM<0 THEN
CPHASE=CPHASE+PPI
ELSE
CPHASE=CP
END IF
END IF
END FUNCTION

Notice that the header says "FUNCTION" instead of "SUB". This code takes two arguments: NUM and DENOM, and returns a value. The value is determined by the "CPHASE=" statements. When you set a function's name equal to a value, you are outputing the value. To call the function, you set some variable equal to the function name (You'll see that below). ATN is a LibreOffice Basic function that calculates the arctangent of some number. ABS is the function that returns the absolute value of a number. This function is basically a decisiion tree that determines what value CPHASE will take.

If DENOM is less than 1E-15 (or 1 x 10-15), then if NUM is negative CPHASE is -pi/2, else CPHASE is pi/2. If DENOM is more than 1E-15, then if DENOM is negative, CPHASE is the arctangent of NUM/DENOM + pi, otherwise, it is just the arctangent of NUM/DENOM.

Here's the subroutine CCUBRT, which calculates the cube roots of a complex number (there will be three cube roots).

SUB CCUBRT()
'Calculates the three cube roots of CMPN1.
'The results are stored in CMPN2, CMPN3, and CMPN4
DIM PPI AS DOUBLE, R AS DOUBLE, T AS DOUBLE, TT AS DOUBLE

PPI=4*ATN(1)
R=SQR(CMPN1(1,1)*CMPN1(1,1)+CMPN1(1,2)*CMPN1(1,2))
R=R^(1/3)
T=CPHASE(CMPN1(1,2),CMPN1(1,1))
T=T/3
CMPN2(1,1)=R*COS(T)
CMPN2(1,2)=R*SIN(T)
TT=T-(2*PPI/3)
CMPN3(1,1)=R*COS(TT)
CMPN3(1,2)=R*SIN(TT)
TT=T+(2*PPI/3)
CMPN4(1,1)=R*COS(TT)
CMPN4(1,2)=R*SIN(TT)
END SUB

The statement that calls CPHASE is:

T=CPHASE(CMPN1(1,2),CMPN1(1,1))

It feeds the values stored in CMPN1 to the function and transfers the result to the variable T.

The part of CompCube that calculates the cubed roots of V is:

CMPN1(1,1)=V(1)
CMPN1(1,2)=V(2)
CCUBRT
U(1)=CMPN2(1,1)
U(2)=CMPN2(1,2)
U1(1)=CMPN3(1,1)
U1(2)=CMPN3(1,2)
U2(1)=CMPN4(1,1)
U2(2)=CMPN4(1,2)

U, U1, and U2 stores the resulting cubed roots.

And the part that calculates the cube roots of W is almost exactly the same except for the variables used:

W(1)=-Q(1)/2-SDET(1)
W(2)=-Q(2)/2-SDET(2)

CMPN1(1,1)=W(1)
CMPN1(1,2)=W(2)
CCUBRT
V(1)=CMPN2(1,1)
V(2)=CMPN2(1,2)
V1(1)=CMPN3(1,1)
V1(2)=CMPN3(1,2)
V2(1)=CMPN4(1,1)
V2(2)=CMPN4(1,2)

The next section collects all the possible roots of the equation. Only three will be appropriate, but the nine possible roots will all have to be tested.

'Find all possible solutions to the formula.

Z(1,1)=U(1)+V(1)
Z(1,2)=U(2)+V(2)
Z(2,1)=U(1)+V1(1)
Z(2,2)=U(2)+V1(2)
Z(3,1)=U(1)+V2(1)
Z(3,2)=U(2)+V2(2)
Z(4,1)=U1(1)+V(1)
Z(4,2)=U1(2)+V(2)
Z(5,1)=U1(1)+V1(1)
Z(5,2)=U1(2)+V1(2)
Z(6,1)=U1(1)+V2(1)
Z(6,2)=U1(2)+V2(2)
Z(7,1)=U2(1)+V(1)
Z(7,2)=U2(2)+V(2)
Z(8,1)=U2(1)+V1(1)
Z(8,2)=U2(2)+V1(2)
Z(9,1)=U2(1)+V2(1)
Z(9,2)=U2(2)+V2(2)

To test these values, I have to use them to evaluate the cubic equation. I use another subroutine for that, and that subroutine requires a function to return the absolute value of the complex number. I also realized that I needed to save the complex coefficients for this routine so I went back and did that in MatArray1, a global array I use to transfer matrices around.

'Load equation into MatArray1
REDIM MatArray1(4,2)
MatArray1(1,1)=A(1)
MatArray1(1,2)=A(2)
MatArray1(2,1)=B(1)
MatArray1(2,2)=B(2)
MatArray1(3,1)=C(1)
MatArray1(3,2)=C(2)
MatArray1(4,1)=D(1)
MatArray1(4,2)=D(1)

The sizes of dynamic arrays like the global MatArrays can be changed any time using the REDIM (redimension) command.

Here are the test routine and absolute value function:

FUNCTION CABS(ARG1, ARG2)
'This function returns the absolute value of a complex number
'ARG1+ARG2 i
CABS=SQR(ARG1*ARG1+AGR2*ARG2)
END FUNCTION

SUB CTEST()
'This subroutine tests a solution set of a cubic equation for appropriate
'values.
'The output is z1=a z^3 + b x^2 + c z + d
'In other words, it evaluates the original cubic equation.
'It gets the equation from MatArray1.
'The value of the complex variable is in CMPN4.
DIM U(2), V(2), W(2), Z1(2)

CMPN1(1,1)=MatArray1(1,1)
CMPN1(1,2)=MatArray1(1,2)
CMPN1(2,1)=CMPN4(1,1)
CMPN1(2,2)=CMPN4(1,2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

CMPN1(1,1)=CMPN4(1,1)
CMPN1(1,2)=CMPN4(1,2)
CMPN1(2,1)=U(1)
CMPN1(2,2)=U(2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

CMPN1(1,1)=CMPN4(1,1)
CMPN1(1,2)=CMPN4(1,2)
CMPN1(2,1)=V(1)
CMPN1(2,2)=V(2)
CMUL
W(1)=CMPN3(1,1)
W(2)=CMPN3(1,2)

Z1(1)=W(1)
Z1(2)=W(2)

CMPN1(1,1)=MatArray1(2,1)
CMPN1(1,2)=MatArray1(2,2)
CMPN1(2,1)=CMPN4(1,1)
CMPN1(2,2)=CMPN4(1,2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

CMPN1(1,1)=U(1)
CMPN1(1,2)=U(2)
CMPN1(2,1)=CMPN4(1,1)
CMPN1(2,2)=CMPN4(1,2)
CMUL
V(1)=CMPN3(1,1)
V(2)=CMPN3(1,2)

Z1(1)=Z1(1)+V(1)
Z1(2)=Z1(2)+V(2)

CMPN1(1,1)=MatArray1(3,1)
CMPN1(1,2)=MatArray1(3,2)
CMPN1(2,1)=CMPN4(1,1)
CMPN1(2,2)=CMPN4(1,2)
CMUL
U(1)=CMPN3(1,1)
U(2)=CMPN3(1,2)

Z1(1)=Z1(1)+U(1)+MatArray1(4,1)
Z1(2)=Z1(2)+U(2)+MatArray1(4,2)

CMPN3(1)=Z1(1)
CMPN3(2)=Z1(2)

END SUB

After all that I had some small bugs (small bugs = disaster) and had to do some fine tuning. Debugging got rid of the bugs and my test subroutine was too sensitive for the tiny round off errors I produced so I had to change the test criterion:

IF X<0.001 THEN
to the more liberal:

IF X<10 THEN

Now, it works, and it work within input precision.

I'll be programming a lot more and it will be available at the Timeline. The basics of LibreOffice Basic are here and they should give you enough to figure out what I'm doing in future programs. I have a large, complex project coming up that will give me a chance to share some of my problem solving techniques with you.