text:
Scot's Newsletter
Operating Systems. Broadband. Issues. Reviews ...
Information You Can Use.
Please Recommend this Newsletter to a Friend.
September 2007 - Vol. 7, Issue No. 93
By Scot Finnie
Apple's Taking a Pass on the Enterprise Prize
Review: Corsair 2GB Flash Padlock USB | Top Product!
Last Word on FiOS TV
Windows Software Firewalls Evaluation Rolls On
Your Input Wanted: Possible Changes to Scot's Newsletter
A View of Our Automotive Future
Upcoming Newsletter Schedule
Subscribe, Unsubscribe, Change Address, Change Format
Read Back Issues
The SNF Forums
Donate via Letter Mail
Have You Considered Advertising in Scot's Newsletter?
It's a great way to support the newsletter and get something valuable in return.
Please Review the Rate Card for more info and send us an inquiry by email.
house advertisement
It's the unofficial motto of the lottery industry: You've got to play to win. A couple of decades ago, the vast majority of microcomputer companies realized that the jackpot was in sales of computers to businesses. Apple opted not to play, and as a result, it had a troubled history throughout most of the 1990s.
Now, for the first time since the Mac was introduced in 1984, Apple has a real opportunity to play to win by focusing some of its resources on selling computers to the enterprise. Apple isn't a large company, however, and it's headed in an entirely different direction, transforming itself from a consumer computer company to a consumer electronics company. But is that truly the right move for Apple? It might be, but it's not without risk. And it may mean passing up a golden opportunity.
Don't believe the siren song emanating from Redmond about how well Vista is doing. It's not doing all that well. That doesn't mean Microsoft is in trouble long term, because as things stand now, Vista (and its mildly improved derivatives) will eventually take over the Windows marketplace and wind up being the largest-selling version of Windows ever.
But there's a brief period of time — a year, perhaps 18 months — in which a determined competitor with a better operating system and a more customer-focused strategy might be able to gain a toehold among enterprise computer buyers.
It's not just the fact that Vista requires powerful hardware and has received a mixed reception that threatens Windows' dominance. A small but growing number of IT pros have begun questioning Microsoft's understanding of and commitment to their needs. The software giant's increasingly self-serving policies, its obsession with software piracy, its inability to create a reliable desktop environment, and the constant need to upgrade software and hardware to keep pace with newer versions of Windows are wearing down the patience of its customer base.
That gives Apple an opening. And on the desktop, the Macintosh offers a combination that no other alternative does:
1. Mac OS X's FreeBSD roots provide a level of reliability matched by no version of Windows and no previous version of the Mac. In other words, it's nearly as reliable as Linux. The improved reliability and the fact that malware is targeted at Windows machines means reduced help desk calls and longer life spans for purchased Mac units.
2. On top of that, OS X has a rich user interface that is familiar to both Mac and Windows users, since Windows and the Mac use virtually all the same underlying UI techniques.
Groundwork Laid, but Where's Apple Now?
Apple's current opportunity in the enterprise marketplace is the direct result of its brilliant work in designing the best-of-breed desktop operating system back in 2001 when it introduced Mac OS X 10.0. More than six years and two versions of Microsoft's Windows later, OS X is still the state of the art in desktop operating systems. OS X has also revitalized the Mac software marketplace (despite Apple's poor showing in supporting independent software vendors).
Last year's switch to an Intel-based architecture capped off a long series of moves by Apple — including aggressive and inexpensive updates of OS X, much better interoperability on Windows networks, and the release of the Boot Camp software for booting Windows on a Mac — that have prepared the company to make a run at the enterprise.
But since then, the focus has been on the iPhone and new iPods. Apple even pushed back the release of OS X 10.5 "Leopard" for that purpose. Cupertino shows no signs of pursuing a serious strategy to grow enterprise sales.
I tried going to the source to find out more. About three weeks ago, I put the question to Apple's PR department: What is Apple's strategy for selling computers to enterprises? The company has had every chance to answer that question; several very good PR people have contacted me, but they haven't had anything of substance to say. The sum total of the direct response so far has been: "Look at the Business page on the Apple website." The Apple Business page is aimed at small businesses, not enterprises. There's nothing on this page of interest to corporate IT pros.
In order to succeed in the enterprise marketplace, Apple has to do several things. It needs an enterprise product line (a thought I'll elaborate on in a moment). It needs enterprise pricing, including a volume-licensing strategy. It needs an enterprise sales force. It needs IT-oriented support policies and quality levels.
It's not clear to me that Apple has the right stuff to sell to the corporate marketplace. Who knows, perhaps Steve Jobs is making the right decision in steering away from the enterprise. Maybe he knows Apple can't succeed there because it just isn't culturally suited to the business of selling to other businesses. But when it comes to building hardware for big business, there's no doubt in my mind that Apple could succeed.
Enterprise Macs
Over the past year or two, a number of experts and pundits — some of whom were writing for Computerworld — have said that Apple's existing product line would work fine as is for IT. But that's just not the case. There are several issues with Mac hardware that keeps it from being ideal.
The MacBook Pro 15 and 17 models are too expensive by about $600 to $700 to compete with top-quality Windows notebooks, such as the Lenovo T60 series. Enterprises need to be able to purchase a MacBook Pro for around $1,900 with 2GB of RAM and a 120GB drive.
While it's very easy to upgrade RAM in the MacBook Pros, it's difficult to replace hard drives. To succeed in the enterprise, Apple needs to adopt user-removable hard drives. That makes it much easier for IT departments to troubleshoot and fix their users' problems without having to crack the case of a notebook — something that's a little touchy with the Macs.
The lack of a docking station option from Apple is also a serious drawback. It may sound minor, but docking stations are used heavily by companies that have adopted the no-desktop approach. Busy employees bring their notebooks home to work there, and also on the road. The docking station cuts down on cable connections, which, in turn, cuts down on help desk calls. At the very least, without the docking station, the 15-inch model needs a third USB port like the 17-inch MacBook Pro.
In some enterprises, a 13.3-inch-screen subnotebook has become the staple computer. Frequent travelers prefer the smaller size and heft of subnotebooks. Apple's MacBook occupies this form factor, but the company designed it for students, not execs.
Like the MacBook Pro 15, it lacks ease of upgradeability for anything but RAM, it has only two USB ports, it doesn't have a docking station, and the screen is small for work on spreadsheets, presentations, and other common business pursuits. Happily, the MacBook does support an external display at up to 1,920-by-1,200-pixel resolution. But back on the downside, the Chiclet-style keyboard isn't appealing to touch typists.
Finally, the MacBook is thicker than the MacBook Pros. In order to build the small and light notebook that many enterprise users crave, Apple should start with the MacBook Pro case and trim it for a 13.3-inch display. It doesn't have to be aluminum, but it does have to look upscale, and it needs the same flexibility and upgradeability I suggested for the MacBook Pro.
IT pros should be able to snag this 13.3-inch MacBook for business at around $1,400 with 2GB of RAM and a 100GB hard drive.
Not every business organization has settled on notebooks, and this is the area in which Apple is the weakest. To succeed in the corporate world, Apple needs to build an expandable/upgradeable desktop Mac that lets IT shops easily upgrade RAM, easily replace the original hard drive, add a second hard drive, upgrade the video, and have at least one free card slot or internal expansion area.
It should also come with a DVI port (and VGA adapter) for an external display. There's no reason why the basic specs shouldn't be similar to those of the iMac. But it needs to sell for $1,000 (without monitor) with 2GB of RAM and a 120MB hard drive.
In case you're wondering, the iMac is not a good business machine. It's barely upgradeable, and enterprises can often get more mileage out of LCD displays than they can out of desktop computers. Besides, if either the monitor or the computer needs to be sent in for service, the other piece must be sent too. The iMac is just not well designed for enterprise use.
By now you've probably gathered that there's very little reason to suspect that Apple takes the enterprise market seriously. Just the fact that we really don't know what Apple thinks about this is enough to say there's no strategy. Business-oriented strategies aren't secretive. That's a consumer marketing thing.
To make it all happen, Apple would have to design and sell new business Macs with a pricing structure aimed at volume sales. It would have to make a stated commitment to take care of its enterprise customers.
In other words, Apple would need to stop looking at corporate computing as a dirty word. The aloofness Apple exudes as a consumer electronics company plays fine to that marketplace, but that doesn't play well in the business-to-business space. IT customers want to be listened to and respected, and they want the company they buy from to offer innovative products and services that address their needs.
A public move by Apple in this direction would generate a ton of interest. Enterprise buyers stake their careers on their purchases. They need to feel the goodwill.
The truth is that Apple already has the goods, both in terms of the Intel-based architecture and the great operating system software. What it lacks is the hardware packaging and services that would make the Mac a lot more appealing to business users.
Even three years ago, it would have been totally laughable to write about the possibility of Apple being able to carve out a beachhead on the shores of the corporate marketplace. Now even dyed-in-the-wool Windows shops recognize that a small percentage of companies are moving this way, even if they think such companies are nuts.
It wouldn't seem so crazy if Apple had some skin in the game. You've got to play to win.
Review: Corsair 2GB Flash Padlock USB Stick | Top Product!
I've been searching for a USB stick with large storage that I can use as my everyday portable storage. I require this device to have security protection. USB sticks are too easy to lose, and I might have sensitive personal or corporate data on it from time to time. I don't want to worry about prying eyes should the darn thing fall out of my pocket.
For the past few years I've been using a highly portable 5GB Seagate USB 2.0 Pocket Hard Drive, which contains a 1-inch mini-drive.
It works with Macs and PCs, but unfortunately the built-in security is software-based, and it requires Windows to run. While almost every USB storage device works on the Mac, most of them are using Windows-based software — not hardware — to encrypt or lock up your data.
So when a press release arrived from Corsair about a self-locking USB stick with a built-in numeric keypad called Flash Padlock, I had to check it out. (See close-up picture.)
The Flash Padlock currently comes in 1GB and 2GB sizes at the list price of $29.99 and $39.99, respectively. I'd love to get one that was at least 4GB and preferably 8GB. I sometimes move large sets of software or images around. The business of software reviews is data-intensive. But 2GB handles most of my needs.
Flash Padlock contains a small, user-replaceable lithium battery that allows the five-button numeric keypad to work even when the USB stick is unconnected to your computer. In fact, you set your combination, change it, lock the device, and unlock it — all while the stick is removed from your computer. This makes total sense; doing anything else would endanger your USB port and also be awkward. The black case offers five number buttons and a Key button (think of it as Enter). There are also two LEDs that light up the Locked and Unlocked icons. The Flash Padlock is about 33% thicker than the average USB stick. The added heft accommodates the battery and keypad, and is well worth the advantage the hardware security brings.
The user interface for the Flash Padlock is very well thought out. Once you set your up to 10-digit combination, the device locks automatically 15 seconds after it is removed from your computer. If you try to reinsert it five minutes later, you'll see the red Locked icon display. To unlock it, you remove the device from your computer, press the Key button, enter the combination, re-press the Key button, and the green Unlocked icon will flash. Then you can insert it into your computer. If you don't insert it within 15 seconds, it will lock again. So long as the device remains in your computer, it will remain unlocked.
The specs for the Flash Padlock include a 30MB/sec. read speed, 7.8MB/sec. write speed, USB 2.0 and 1.1 support, and support for Windows, Mac, and Linux platforms. The Flash Padlock's read speed is about as good as it gets, but the write speed is only average. If write performance is your goal, check out the Lexar JumpDrive Lightning as a starting point. It offers sustained write speeds up to 21MB/sec. For my purposes, a faster write speed would be a nice-to-have, not a have-to-have. Plus, in real-world tests, the Flash Padlock's write speed bested that of my Seagate 5GB Pocket Drive. Still, I look forward to the day when flash storage makers realize that buyers of premium USB sticks want the whole enchilada, not just speed OR security OR readouts OR durability.
The downsides to the Flash Padlock are few. The stick is a little thicker than the average USB stick, as mentioned. The rest of the dimensions are typical of enterprise-oriented USB sticks. I don't consider the extra size to be a serious drawback, although the device lacks curbside appeal as a result. The stick comes with a removable cap that is sure to be lost sooner or later. The biggest shortcoming is that Corsair isn't offering a larger-capacity version of the Flash Padlock.
I asked Corsair via email whether it planned to offer a larger-capacity version of the Flash Padlock. Corsair vice president of marketing, Jack H. Peterson, responded through the company's PR agency by writing: "Possibly. The issue is that if the user loses [his or her] PIN, the drive becomes 'useless' and might as well be thrown away. Like the locks from your school days, if you forgot the combo, you just threw it away –- but then again, such a lock was not that expensive. We are assessing the mindset of a user throwing away a $60+ item."
I think I can speak for a large portion of Corsair's prospective customers when I say: We don't care give us more GBs now! In my very first test of Flash Padlock, I got the error message telling me there wasn't enough storage space to accommodate the files I wanted to transfer to the lockable USB stick. Besides, Corsair has a solid way to protect you from forgetting your Flash Padlock password. The company website provides a page where you can register your PIN and retrieve it later if you forget it:
It's also possible to disable the password protection. In fact, the device is shipped with the locking feature disabled; you enable it by setting your password. I don't know about you, but I don't want a USB securing device that has a back door. I like that I can lock myself out if I'm dumb enough to forget my password. The Web-based storage of my password means that even if I forget the password when I travel with the Flash Padlock, I can still retrieve my password — without keeping the password written down on paper.
The Flash Padlock's hardware-based security is a definite advantage over the software security offered by most other USB flash drive makers. No secure USB stick is truly secure, but I'd place my money on the hardware-based security over software solutions. Plus, this method also enables support for Linux and the Mac. The locking mechanism is also very well thought out and easy to use. My retail-packaged evaluation unit came with two sets of directions, and the Corsair website offers online tech support (although the brand new Flash Padlock wasn't yet listed among the products at press time). Corsair offers a three-year warranty.
Bottom line? I'm hanging up the Seagate Pocket Drive and making the Corsair Flash Padlock my daily-driver portable storage device. Kudos to Corsair for thinking outside the box to take USB security in a new direction. I may just have to get two of these until Corsair comes out with a 4GB version of the Flash Padlock.
Fact Box
Top Product! | Flash Padlock 2GB, Corsair, 510-657-8747, info@corsairmemory.com, $35 street price
Over the past few issues of the newsletter, I've discussed the pluses and minuses of Verizon's FiOS TV service. I've had Verizon's FiOS Internet service since January of 2006, but I recently decided to skip Verizon's FiOS TV digital entertainment package. I decided to upgrade my existing Comcast digital cable TV service to support my flat-panel HDTV. To get caught up, check these articles:
Good News About FiOS TV
FiOS TV Has Drawbacks
Last time, I offered something of an apology for my earlier surmise that there might be some impingement of broadband Internet throughput because Verizon also uses that pipe to send video-on-demand programming, such as movies and shows. In my household, I have five televisions and set-top boxes connecting them to digital programming. My kids are addicted to on-demand children's programming, and my oldest loves to order free on-demand movies. Personally, I'm more into Netflix. But there's a lot of on-demand programming going on in the Finnie household. So I got to thinking: Would multiple on-demand TV programming coming down the pipe diminish the bandwidth available to my Internet connection?
It turns out that my concern actually does have some merit, although only in the worst-case scenarios.
Since the last issue, I was contacted by Frank Boersma, director, set-top box and in-home network engineering at Verizon. He explained the way FiOS TV and FiOS Internet work together in a telephone interview. We followed up the interview with this question and answer in email:
Scot: "My understanding is that the video on demand (VOD) can use the full bandwidth available to my connection while the Internet connection is capped to whatever service level you ordered. In my area, for example, the most you can get is 30Mbps, and my Internet broadband service is capped at 15Mbps. But what if I had ordered the 30Mbps Internet service level? Wouldn't my Internet connection be reduced whenever VOD was playing?
Frank Boersma: "If your FiOS Internet speed was provisioned at 30Mbps, and that matched the limitation for your area, you could see an impact to your Internet speeds using VOD. That would only occur if you were watching VOD while attempting to access a website which could reach speeds of 30Mbps download. Each VOD session takes roughly 4Mbps of bandwidth from your permanent virtual circuit (PVC), so a single VOD session would leave 26Mbps available for Internet downloads. It's more likely you could see an impact to your data speeds if you had multiple VOD sessions going at the same time (e.g. 4 VOD sessions simultaneously, which would use about 16Mbps). FiOS TV customers are limited to a theoretical maximum of seven VOD sessions (28Mbps), which in your example would leave only 2Mbps for Internet traffic."
So it appears that if you use a lot of on-demand programming simultaneously (and my family does sometimes) there could be some fallout.
There's another piece to the puzzle worth noting. In some areas, FiOS is capped at 30Mbps while in others it's capped at 50Mbps. Last I checked, my home is still in a 30Mbps area. If you have 20Mbps FiOS Internet and you're in a 50Mbps area, there's no way your Internet connection could be affected by heavy simultaneous usage of video on demand. In my case, I've been trying for some time to get Verizon to upgrade my FiOS Internet service to 20Mbps. I was provisioned at 15Mbps and, due to a problem with my account, they haven't been able to raise me to 20Mbps.
That problem with my account, which I've detailed in the past, amounts to this: For whatever reason, as a very early FiOS customer Verizon's account system wound up listing me as a DSL customer, not a FiOS customer. I'm being charged the introductory FiOS charge (about $37 a month) for my 15Mbps service, which is a good deal less than the $49.99 Verizon now charges (with a subsequent rate hike) for its 20Mbps service. I have tried about a dozen times since my early 2006 FiOS Internet installation to get this fixed. So far, Verizon has been unable to resolve it.
So, anyway, if I ever get to 20Mbps FiOS Internet in my 30Mbps-capped area, that would leave only enough bandwidth to have two simultaneous video-on-demand programs playing. With three or more programs playing, my Internet connection would be impinged on. So, my situation is close to being the worst-case scenario. I think I made the right choice in sticking with Comcast for digital television. But your usage and your provisioning from Verizon could be very different.
If you're considering Verizon TV, I think you should make sure to ask your Verizon rep what FiOS is capped at in your town.
For about a year now I've been researching software firewalls for Windows. There are at least five previous installments in this series, and several early contenders have been dropped from my prospect list, which has been winnowed down to one or two products in beta. (For links to previous installments in this series, see the end of this article.)
I stopped short of naming Comodo Free Firewall 2.4 the Best Software Firewall of 2007 in the last issue of the newsletter because several SFNL readers reported issues they're having with Comodo. I asked readers last time to send me their experiences with Comodo, and thank you, many of you did just that.
The results of that little exercise were interesting. Many people are having no issues with Comodo's 2.4 firewall. That included me at my last writing on this subject. Since then, I have had some of the problems others describe on one of the now five Comodo installations I've been testing. Not the worst of the problems, mind you. But at least I'm no longer totally in the dark. And I've also worked with two or three SFNL readers to the point that I'm satisfied that their reconfiguration of the product isn't causing the symptoms they're having.
There are three different problems with Comodo 2.4 reported by sufficient numbers of readers (also posted elsewhere on the Internet) to make me think they are actual bugs:
1. Comodo forgets user inputs in user permission pop-up boxes. Comodo offers a "remember this" check box, but checking the box doesn't appear to work.
2. Comodo throws off a blizzard of user-permission pop-ups — so many pop-ups that most users don't even last 24 hours before uninstalling Comodo.
3. User's system slows down dramatically after install.
The only problem I've seen personally is the first one, and only very recently. I was able to make the second problem occur by making a settings change to Comodo away from the default setting. If you're seeing a blizzard of Comodo pop-ups, try making this change:
Click the Security button along the top of the Comodo program. Then click Advanced on the left. Then click Miscellaneous on the bottom. A dialog box will open. Set the Alert Frequency Level to Low. That's the default setting.
A large percentage of the people who've written to me to complain about Comodo 2.4 will see significant improvement of the user experience with this step. About the first problem, though, the only suggestion I can make is to uninstall and reinstall the product.
At the end of July, I interviewed Comodo's president and CEO, Melih Abdulhayoglu, and senior research scientist Egemen Tas. This is a pretty rare thing, but they readily admitted that some Comodo 2.4 users are experiencing the first two problems described above. Instead of trying to fix version 2.4, they said that version 3 (under development now and currently projected to be released in October) has been entirely rearchitected so that these problems won't reoccur.
The strategy Comodo is employing for version 3 to block malware is different from any other product I'm aware of. Comodo 3 adds a host-intrusion prevention system (HIPS). If you've ever tried a HIPS, you probably know that on the desktop, such a system would probably add pop-ups and warnings. To make it easier to work with, Comodo is adding two features — whitelist and program profiling — that when combined should eliminate many pop-ups and warnings. Comodo 3 will be able to online updated with new information to support these features, and presumably users will be able to add their own intelligence about accepted program behaviors.
I'm not 100% convinced about this strategy, but I've decided to look at version 3 before I come to a decision. An early look at the first beta of Comodo 3 shows that the program has been heavily upgraded. But since the whitelist and profiling features haven't been added yet, the product is all but unusable.
Meanwhile, Eset recently released Eset Smart Security Beta 2, which combines Nod32 with a new lightweight software firewall and an anti-spam tool. Beta 2 adds direct support for Outlook Express, in addition to Outlook. I have not had a chance to test Beta 2, but this suite — which did not do well in my leak testing of an earlier beta — is still a possible contender for me.
Previous Installments in the Software Firewall Series
Twists and Turns on the Road to the Best Software Firewall (July 2007)
Review Roundup: Slim Is in for Windows Desktop Firewalls (June 2007)
More on Software Firewalls for Windows (June 2007)
Update: Software Firewalls for Windows XP (April 2007)
Kicking off a Software Firewall Comparo (Sept. 2006)
Those of you who have been reading this missive for a long time can probably recall that every two or three years, I try to come up with a new plan that will save me time on producing the newsletter. My life has become more complex over the past three years. I now have three children. I no longer work out of my home. And I have a challenging management job with a lot of responsibility that eats up my time. It's no longer possible for me to describe my work at Computerworld as just a "day job."
When I started the newsletter in 2001, it was a weekly. A few years later I had to cut back to every other week. A few years ago, I was forced to cut back to monthly frequency. This year I've only been able to publish six issues counting this one.
Other issues make the newsletter business very tough. In my Let's Fight Sp@m series, 2002-2004, I more than once articulated my concern that spam — and the way ISPs, the government, anti-spam solutions, and corporations are attempting to fight it — will eventually kill the viability of email-based newsletters. I was clearly right about that. On the Internet, email is guilty until proven innocent. And most people don't care enough to ferret out the truth. Four years ago, many people believed that RSS would replace email newsletters. And in a way, that's partially true. What has actually replaced newsletters is blogs (most of which have RSS feeds).
So, let's cut to the chase: I am once again considering relaunching Scot's Newsletter as a blog, using the Scot's Newsletter HTML and Text lists as announce-only notification of major blog entries. The last time I suggested something along those lines, I got bags of mail about it that split into two extreme camps. Many of you preferred that kind of approach; but many of you really hated the idea when I asked a similar question about two and a half years ago. I'd like to check the point again, so please send me your vote.
Should Scot's Newsletter become primarily a blog?
Vote Yes (click to email your vote).
Vote No (click to email your vote).
The subject line of your email will tell me your vote, but inside the message, I have a question for you to answer:
If You Vote Yes: Do you like the idea of being notified via an email when a significant new blog entry or entries have been posted? Or would you prefer to stop receiving email entirely?
If You Vote No: Would you be willing to switch to the HTML format?
Making the Entire List HTML
The vast majority of people who try the HTML edition of Scot's Newsletter prefer it. Because I have to create two completely different versions of the newsletter every time I send it — Text and HTML — eliminating one of the two editions would also save me a good deal of time and aggravation. It would also save me money because maintaining two lists costs more.
Some of you, I know, are thinking: Great, so kill the HTML edition. There are two problems with that line of thinking. The first is that only 15K of the newsletter's 45K subscribers are taking the Text edition. All the rest are HTML. The second is that in order to create the HTML archive, I have to create the HTML edition. So sending only the Text edition not only doesn't save me time, it would upset a larger number of subscribers.
My gut tells me that making the big switch to a blog is the right move for Scot's Newsletter and Scot Finnie. That would allow me to write blog entries periodically, throughout the month. Knowing me, I'd probably also write several larger things all at once, the way I do now. That's just the way I'm used to working. But having the freedom to publish any time would make what I write more timely. And by spreading out the load of content — and greatly reducing the production overhead — I would be able to spend more time on content and less time on managing the newsletter.
Incidentally, from a business perspective, the newsletter's mailing list is the thing that has the most value to me as a writer. Even if I use the list as a notification tool, many of you will probably unsubscribe. Also, the newsletter's potential advertisers will probably be far less interested in Web-banner advertising on a website unless and until the new blog has a loyal readership with gobs of page views. I have no illusions about the business aspects. I never have. The newsletter is a labor of love, so a blog would just be an easier-to-manage labor of love. To me, that seems to be a worthwhile tradeoff.
I have not yet made a decision about this. And no matter which way I go, it will likely take me a few months to find the time to act on whatever I decide. But something has got to give. My call for your vote is your chance to give me your insights about what you think would work best.
Almost two years ago, in the wake of hurricane Katrina and the subsequent rise in gas and oil prices, I made the GasBuddy.com peer-based gasoline filling station price site Link of the Month. Then I took the opportunity to write Gratuitous Rant About the Auto Industry, which focused on the government's toothless CAFE standards and the auto industry's lack of initiative in aggressively exploring alternative-fuel-based systems.
What followed the opinion piece was an intriguing bevy of email from Scot's Newsletter readers both agreeing and disagreeing with my viewpoint. Many of the messages I received were from people who work as engineers and scientists in the power and automobile industries.
One of the more common refrains was that hydrogen/oxygen-based fuel-cell-powered automobiles just aren't a realistic alternative. I disagreed, and still disagree — but I recognize that hydrogen has several hurdles to leap. We're certainly a long way from being able to just switch to hydrogen-based fuel-cell vehicles.
One of the advantages of hydrogen is that it's the most abundantly available chemical element in the universe. Stars and gaseous planets contain high quantities of hydrogen, but very little gaseous hydrogen remains naturally in our atmosphere. Of course, water is made up of two hydrogen atoms and one oxygen atom, creating the most abundant molecule on the Earth's surface. So, to become a readily available fuel source, hydrogen would have to be produced through some sort of thermo, electrical, or chemical process requiring a power source. The most probable process would be to break down water to unleash its hydrogen.
According to an article in the October 2007 issue of Consumer Reports, hydrogen is currently produced primarily by burning natural gas to heat water, although using electricity for the same purpose is another possibility. Neither method is ideal, since both deplete nonrenewable resources today. In many parts of the U.S., electricity is made by burning coal or petroleum. We're a long, long way from being able to use electricity derived from solar or wind power to generate hydrogen. On the other hand, we've got a much better chance of figuring out how to do this more efficiently in, say, 30 years than anything else on the horizon.
The need for another power source to generate hydrogen is its most important shortcoming. If we conquer the first problem, the next-biggest issue is significant but by no means insurmountable. According to Consumer Reports, there are only 44 hydrogen filling stations in the U.S. In order to make any switch to hydrogen a reality, we would have to overhaul our automobile support infrastructure, something that isn't going to happen overnight.
But consumer demand could accelerate the process. What if oil were $150 a barrel in today's dollars? What if gasoline were $10 a gallon? If our technology improves enough that we can produce hydrogen in sufficient quantities, using significantly less nonrenewable resources, with cleaner processes, and at a clear cost savings to the consumer (meaning that the cost of fuel-cell autos weren't too much higher than gas-powered ones and hydrogen's cost per mile were less than that of gasoline), the transformation of the infrastructure would occur rapidly, perhaps in as short as a decade.
None of those suppositions is impossible, either. We have the technology now to make fuel-cell vehicles go. Current hydrogen fuel-cell test vehicles deliver power at an acceptable rate, according to Consumer Reports. Higher pressure hydrogen tanks are under development that will increase driving range and reduce the size of the tanks. They use batteries, like hybrid vehicles, and although battery advancement moves slowly, the demand for batteries is skyrocketing worldwide. A smaller, lighter battery technology could be an important breakthrough for hydrogen-based cars. Finally, fuel-cells are an efficient source of power, and they're liable to become more efficient, smaller, lighter, safer, and much less expensive as several manufacturers compete to produce them.
Fuel cells also convert hydrogen and oxygen into electricity without harming the environment. The primary byproducts of the fuel-cell process are heat and water, so it's very clean. Several automakers have hydrogen-based fuel-cell prototypes, including Chevrolet, Ford, Honda, Hyundai, Mercedes, Nissan, and Toyota. Consumer Reports points out that General Motors and Honda will soon be placing more than 125 fuel-cell cars into the hands of U.S. consumers to use on a trial basis in California, New York, and Washington D.C.
I have not been behind every initiative of the Bush administration, but this one makes sense to me. Our government has pumped major money into developing both the technology and infrastructure to support fuel-cell automobiles. Right now, it's our best alternative. While I don't expect it to arrive in earnest much before two decades from now, fuel-cell vehicles are probably our best hope for reducing our dependence on petroleum.
In the meantime, hybrid electric/gas-powered vehicles are the next best alternative. If you can afford to buy one when making your next car purchase, I urge you to do so.
Dovetailing with Today's Hybrids
With all this in mind, my wife, Cyndy, and I recently bought a 2007 Toyota Highlander Hybrid. The well-designed midsize SUV combines a 3.3-liter V6 gas engine with two electric motors (it's all-wheel drive), a continuously variable transmission, and a nickel-metal-hydride battery. Although our new Highlander has less than 1,000 miles on it, Cyndy has already managed to drive it up to 34 miles per gallon on her way to and from work. The EPA rating is 31 mpg around town and 27 mpg on the highway. Cyndy's route to work is all back roads, where hybrid vehicles excel on gas mileage, but 34 mpg is pretty good.
It's not just about fuel savings either. The Highlander Hybrid is classified as a SULEV (super ultra low emission vehicle), meaning that it produces far less harmful emissions. Given our clear problem with global warming, whatever any of us can do to clean up our acts is important.
For those of you who doubt that the climate change we're experiencing is man made, I think you've most likely been taken in by the powerful lobbies of vested interests, which include some corporations that like things the way they are. Global warming is real. It's dangerous. And it's time we did something about it.
Back to my little part of the world. Downsizing as my wife and I did from a large gas-guzzling SUV to the Toyota Highlander Hybrid is only painful in the money department. In other words, Toyota's design makes excellent use of the available space, but at a price. So when we learned the redesigned 2008 Highlander Hybrid (due out in a month or two) would be significantly more expensive, I pressed forward very rapidly to grab the 2007 vehicle we bought — it was the only 2007 Highlander Hybrid my dealer could find in my area that met our requirements.
The Highlander is among the more expensive hybrids on the market, but Honda and Toyota both offer less expensive hybrids (the Civic Hybrid and Prius) that sell for around $23K to $26K. Honda is discontinuing its Accord Hybrid for 2008, but the recently designed Toyota Camry Hybrid is a superbly designed vehicle with a spacious interior that sells for $31K fully equipped. Whenever I can afford it, I hope to buy a second, smaller hybrid vehicle.
From a driving perspective, the Highlander Hybrid is a delight. It's got plenty of power (this model splits the electric advantage between fuel savings and performance). The Highlander starts in silence and takes off on battery power unless you floor it. The gas engine takes over at about 12 miles per hour on level ground. Going up a hill or under heavy acceleration, motive power combines the gas engine and electric motor, although usually for only brief periods of time. When you coast or brake, the battery is charged by the turning wheels.
I've driven the Toyota Prius, Camry Hybrid, and Highlander Hybrid. They all work roughly the same way. If you want to drive them as if they're conventional gas-powered vehicles, you can. The only pronounced difference is the sounds they make. This is especially true of the Highlander Hybrid. The power is always there if you want it. The Camry and Prius are optimized for gas savings over power. Neither is a barn burner, although the Camry has plenty of power.
Many people who own hybrid vehicles — especially the Toyota models — have found that driving for fuel efficiency becomes something of a pleasant game. Toyota offers graphical indicators that tell you how your hybrid's engine is working at any given moment (coasting-charging, operating on battery, operating on gas engine, and so on). It also gives you a real-time readout of your gas mileage. There's something about having all this information at your fingertips that makes you want to drive your vehicle to save gas. It becomes very obvious to you right away that the way you drive plays an important role in how efficiently your car gets you from point A to point B.
The 2008 Toyota Highlander Hybrid has a new set of features, including several operational modes that vary the way it drives, including an acceleration-smoothing economy mode. You can even set it to disable the gas engine entirely, although the batteries will only last a short time, according to Toyota's press release. I have yet to try it, but it seems to be on the way to something I've wished for on hybrid models — a simple driver-selectable, three-position switch that offers Economy, Mixed Driving (the default), and Performance modes.
To wrap this all up: If hydrogen-based fuel-cell vehicles are, in fact, the future of automobiles, then the gas/electric hybrid vehicle of today represent a baby step toward that goal. The sooner automakers and motorists become more familiar with the use of stored electricity and electric motors to provide motive power for cars, the easier the transition will be to future technologies, such as fuel cells.
Because petroleum is a limited resource, we have a necessity that requires invention. The only sure way to get there is to start by accepting the need and working toward a solution. No one knows exactly when the oil will run out. The problem is, it may take a lot longer than we have to fully free ourselves from the need for this form of power. We have to reduce our usage of this nonrenewable resource as soon as we can — to give ourselves more time to figure it out. Right now, with newly industrializing countries around the globe beginning to emerge, we're going in the opposite direction. It's time for a change.
To sound off about this topic to me, please use this email link.
Some Sources:
Los Alamos National Laboratory's Periodic Elements - Hydrogen
U.S. Department of Energy Hydrogen, Fuel Cells & Infrastructure Technologies Program
Consumer Reports - The Ultimate Green Machine
Consumer Reports - Hydrogen: A Long Wait
Newsletter Schedule
Scot's Newsletter is a monthly e-zine delivered by email. My aim is to send each issue near the second Tuesday of each month.
The next issue of the newsletter is currently planned for early October.
You can always find out when the next issue of Scot's Newsletter is expected to appear by visiting the Scot's Newsletter home page.
If you like this newsletter, I need your help spreading the word. Please tell your friends and co-workers, and encourage them to sign up! It's free.
While you're at it, visit the new Scot's Newsletter Forums.
Subscribe, Unsubscribe, Change Email Address or Message Format
You can unsubscribe at any time; I don't believe in captive audiences. The website subscription center is the easiest way to manage your Scot's Newsletter subscription. Changes take only a minute or two. You must select your message format — Text or HTML — even for address changes or unsubscribes. All subscription changes are handled on the same page with a database-look-up feature that ensures greater accuracy:
The Scot's Newsletter Subscription Center:
https://scotfinnie.com/snl/subcenter/subscribe.htm
To help with the cost of creating and distributing the newsletter, I accept contributions via PayPal and by check via conventional letter mail. For more information on donations:
Sign up for PayPal (if you don't already have it)
Option #1: Donate via PayPal
Option #2: Donate via Letter Mail
Send comments, suggestions, or questions about this newsletter. Don't be bashful about telling me what you like or don't like. Send emails related to editorial content (only) to scot[@]scotfinnie.com/snl.
Please address advertising inquiries (only) to: sales[@]scotfinnie.com/snl
Sign-up for PayPal.
Support this Newsletter by Donating Today.
Or donate via Letter Mail.
How to Link to Scot's Newsletter
Copyright © 2001-2007 Scot Finnie. All Rights Reserved.
Ten Myths About Copyright Explained.
You are subscribed to Scot's Newsletter HTML EDITION as: $subst('Recip.EmailAddr')


text:
Building social and ecological resilience to face environmental threats: a story from the Triem Tay village
Published by Rusila Drekeni at December 18, 2019
sustainability in tourism
Sustainable Development in the Pacific
Triem Tay is a village in the island of Cam Kim, surrounded by the river Thu Bo, three kilometres away from World's Heritage town Hoi An, Vietnam. The location of Triem Tay is prone to floods during rainy season (water can reach up to 3 meters), washing away entire ecosystems and crops, dramatically increasing the riverbank erosion and forcing inland migration of local residents.
In 2015, Triem Tay got help from the International Labour Organisation (ILO) and UNESCO to promote ecotourism in the island together with the cooperative of villagers. One of the results of this plan, was the concept of floating restaurants as an innovative way to draw visitors to the island. As a whole, this project allowed people to adapt to their current situation and provide work in the village through tourism, but lacked on giving the population the tools to solve the real issue: acting against land erosion.
However, also in 2015, a group of visionaries got together and bought a piece of land in one of the river banks that most suffered from erosion. Here they created An Nhien Farm, a place where natural river banking restoration, regenerative gardening and sustainable skills education merge to create opportunities for nature, and for the people.
Building social resilience
"At first our goal was to resettle the human lives in a deprived area where due to environmental and political issues, the community was kind of forgotten", Hanh Vu tells me, as we sat at dusk at the front of my cabin. She runs the farm, which is also the hub of The Green Youth Collective, a social enterprise lead by her and focused on working with the youth and the local farmers to develop sustainable entrepreneurial skills.
Hanh´s background is as exciting as the story behind the farm. For years, she has been actively engaging in grassroots initiatives on sustainability and climate change adaptation around Vietnam. Until she discovered Triem Tay. Three years ago, she decided to move to the village from Hanoi to understand what shapes people in the area, both naturally and socially.
"It was not easy" she remembers, "even if you speak the same language, it is hard to be accepted in a community where you are seen as a stranger. However, our aim was to involved everyone and work together. We did not want to be seen as a group of people that came from the big city and impose their own way of doing things".
Although the farm is the heart and soul of the project, The Green Youth Collective is their tool to gather farming traditions and ancestral uses of the land. The project it targeted to farmers, youngsters and individuals interested in sustainability as a way of living and working with the land. Regardless of their ethnic and economic background, the farm is also able to provide scholarships for disadvantaged local young people. "The idea is that we do not aim to produce, but to give back to the soil. This way volunteers learn how to work with nature and how to share, avoiding pressure on the ecosystems and being able to replicate our model when they return home", says Hanh.
Building ecological resilience against land erosion
Their approach towards land erosion has focussed on building resilience through regenerating the ecological system of the river bank. By using native species of grass and mangrove, which root system can withstand floods, a natural barrier that stops erosion is created.
How is this natural barrier created? The answer is bamboo and bio-locks. Bamboo is used as the first bio-lock, as it is flexible enough to take the water in an out to protect the mangrove forest. One this is settled, the roots of the bamboo are used for the second bio-lock following the first one. This locks are finished by the implementation of a giant net of bamboo bricks where the mangrove and native grass will be planted. When these species grow strong, have the potential to keep the land and withstand floods. "After these actions, we get nature's feedback. You just need to watch and see how the river bank reacts to what you are doing. It's all about observation," Hanh explains.
However, late in 2017, a man-made flood unexpectedly shocked the village before the rainy season, taken away the young mangrove forest whose roots where not deep enough to withstand a flood. Everything but the bamboo barrier got washed away by the disaster. However their river bank stayed almost intact. This was not the same in the neighboured bank, where land erosion was significantly evident.
The flood proved right one thing: their approach towards building resilience was on the right path. "The local authorities understood that we had developed a way with the greatest potential to stop land erosion and we are now in the process to work with them and create the same system in their land", Hanh tells me with great hope.
The role of the farmstay program
Today, An Nhien Farm is back reforesting the river bank and they also began offering accommodation and experiences to visitors in effort to fund their programs. The farm not only counts with wooden cabins in the bamboo forest, but also with experiences that allow the visitor to connect with the land together with the current volunteer team.
During my short stay, I meet with daily inquisitive visitors who came to find out more about Anh Nhien's Farm approach towards building resilience. Over the last year, hundreds of people have visited the farm to learn about their work. "It is with my greatest pleasure that I explain out methodology to anyone interested. It would be amazing if we can be of an inspiration to other people facing similar challenges", Hanh says.
Vietnam's tourism is experiencing a rapid growth in the last few decades. This is already presenting many challenges that will only keep on growing in the mid to long term. An Nhien Farm is a clear example of how regenerative tourism takes place in the middle of the Vietnamese tourist track. Staying here and learning the value of working with nature and providing the tools for it to thrive, creates a transformative experience in times where the human being is more disconnected from nature than ever before.
Vanuatu, Palau and Cook Islands Stand Up for Sustainable Tourism
Vanuatu Sustainable Tourism Policy Launched
Tonga Uses Big Batteries to Reach Ambitious Renewable Rnergy goal

redpajama_set_name: RedPajamaCommonCrawl


@eggandburger cool! Got any projects going right now?
@joeld Yeah. This main one is a language for creating, editing, and querying timelines of events. It's written in Chicken Scheme. I haven't decided when to put it on github. How about you?


ARIS Vietnam joined Vietnam ITO Conference 2017 (October 20, 2017, HCMC). The big event presented software development capacity, the ability to provide IT services, such as SMAC, AI (Artificial Intelligence), IoT, expanding investment and cooperation opportunities between our country and other countries in the world.
Besides, ARIS Vietnam also attended the Business Matching party with foreign companies, in order to expand relationships and introduce services to the partners of ARIS.


College Recruiter recently recognized the Quinlan School of Business as one of the 12 "Hidden Gem Index" winners for 2015. This distinction identifies the top colleges and universities that employers look at for potential hires.
Akmal said it was a real honor for his team to be honored in such a competitive field. As part of the recognition, he and Wren Donofrio, assistant director of Business Career Services, participated in a webinar to discuss what differentiates Quinlan from other colleges and universities.
They covered topics ranging from career readiness to the dos and don'ts of recruitment. Watch the webinar below.
Another reason for his team's success is programming. He said attractive programming pieces allow students to stay engaged. View the Quinlan events calendar.
The meet-and-greets ran till mid-September. And that leads into Career Week, which is employers' first opportunity to get a glimpse at Quinlan students.
It's also an opportunity for employers to get an idea of the students they'll be meeting. Akmal said sometimes it's easy for a student to overlook a particular company, so Career Week gives employers a chance to make more connections with students.
Following Career Week is the Career Fair, which begins Sept. 24. Last year's fair had almost 80 employers with 500 students in attendance. According to Akmal, it was the biggest one in Quinlan history.
Leading into November, employers will work with students—accepting resumes, holding interviews, and potentially extending offers for internships and full-time positions. And then the whole process is repeated in the spring.
Want to register for career events or look for a job or internship? RamblerLink provides a one-stop shop for job searches, recruiting and more.


text:
Environmental Impact Through Volunteerism
Last updated: May 24, 2017, at 3:51 p.m. PT
Originally published: May 24, 2017, at 3:46 p.m. PT
When Tyrell Dozier packed his bags to head to YMCA Camp Colman as a Counselor-In- Training (CIT) the summer before his junior year, he thought it would be the worst summer of his life.
But what started out as mandated volunteer hours to graduate high school turned into a commitment to bettering his community.
Tyrell went back to YMCA Camp Colman for two more years after that summer. After he graduated high school he worked as a program director at his local Y while attending college, where he was first introduced to the youth-led environmental service program YMCA Earth Service Corps, funded by donors like you through the Annual Fund Drive.
Nearly six years since his CIT days, Tyrell now volunteers as a program instructor, empowering students to take a hands-on role in creating a healthier environment in their schools, communities, and region while nurturing a respect for diversity within the natural world.
He meets with school environmental clubs and helps them plan and execute environmental service projects in their community.
"I'm passionate about ethics in general, and want to make the world a better place," says Tyrell. "Environmentalism issues are big, and hard to tackle. Being able to empower these students and give them the tools to feel like they can tackle these big global issues is so rewarding."
The more than 1,000 youth in the program have an impressive impact on the community. Last year alone, they contributed more than 9,800 hours of service, and removed 202 tons of waste (the weight of 34 elephants) from the earth. But for Tyrell, some of the most rewarding benefits of the program are harder to report on.
"It's hard to measure confidence, and leadership development, and passion," says Tyrell. "But that's the kind of stuff that's happening in this program. That's a sign that something good is happening."
Locations: YMCA Camping & Outdoor Leadership
Category: Volunteer Y Stories Education & Leadership Community Outreach
Swim Lessons: Preparing for your First Day
Summer Group Exercise Challenge


Each package comes with studio time, 2-3 hours of editing, print release, and private link to download full resolution images. Prints, canvas, or metal artwork are also available for purchase.
30 minutes studio session, 7 images, print release, full resolution digital downloads.
30 minutes studio session, 12 images, print release, full resolution digital downloads.
1 hour studio session, 17 images, print release, full resolution digital downloads.
1 hour studio session, 22 images, print release, full resolution digital downloads.
After reviewing your proofs, if there are additional images you like, you may purchase them for $10 each.
Add an offsite location to any package for $50 within a 10 mile radius of Fort Collins, CO.
Add a fantasy keepsake photo to any session for $75 per person.
I also offer high quality prints, canvas and metal art of your photos in 8x10's and bigger.
Have all your photos put on a personal thumb drive for easy access and mobile printing capabilities.
Real estate photography. $150 per home.
$50 for 5 images and then $5 per product image after that.
Get family photos restored! Restorations are priced per project.
Creations of album or book covers are priced per project.


Ascent has a cool name and a cool vibe. I had reviewed their Native Whey protein a few years ago at this point.. geez. Years. I'm getting old. Lol. But yeah it was a long time ago. I thought that their protein was good on the inside, nice looking on the outside, but a bit over-priced at the end of the day. I'm going to go over this pre workout very linearly based off of my experience with it this morning (I actually just received it this AM).
I will go over everything from unboxing to drinking (flavor evaluation) and then take you through how it worked for me in the gym. The final section will be analyzing the ingredients. But let's get started.
I think Ascent has had a cool look from the start. Bold yellow and black colors make it really easy to recognize this quirky brand apart from others. Their signature graphic in the background of each product is a topography lines pattern that echoes the brand's 'outdoors-y' vibe.
I still hate that Ascent still uses these stand-up pouches for their packaging. Like. I really hate it. I think it looks so low-end and given how much Ascent charges for their products, it doesn't make a whole lot of sense to me.
So graphically and in terms of fonts, layouts, and colors, I would give Ascent a 9/10. But these stupid stand-up bags make me drop down their overall packaging review grade to an 8/10. Let's move on to flavor.
There are two Ascent Pre Workout flavors, and I actually bought both of them so I could review each. Before my workout today I took one half scoop of each mixed into about one cup of water. That seemed to be approximately the proper ratio for the level of taste that I wanted.
Watermelon: The watermelon flavor was sweet, just like Ascent directs on their website, but in my opinion it was lacking enough real watermelon flavor. It was as if they were jut a little too stingy with the flavoring materials. But overall it was fine. I did get a tiny bit of stomach discomfort from it I think from the monk fruit sweetener.
Raspberry Lemonade: The raspberry lemonade flavor is described as 'tangy' on the Ascent website but I found it to actually be about the same level of tart as the watermelon. I mean. Yeah the lemon has a bit more natural tartness to it but in terms of acidity and bite I found them to be more or less equal.
The better flavor here is probably the Raspberry Lemonade. For the watermelon flavor to really work I think Ascent needed to be more heavy-handed on flavor. But. Yeah. I guess overall these are okay. I think you can tell by my tone that I haven't been blown away by the Ascent Pre Workout flavors. I will give them an 8/10, and it would probably be lower if I wasn't in a good mood, lol.
After I took both of my half-servings of Ascent Pre Workout Clean Energy and Hydration fuel, I set off for the gym to explore the capabilities of this pre workout. AKA to get a freaking awesome workout in.
I started with some light jogging before doing a very quick yoga flow to get my joints moving properly.
I swear as I get older it takes more effort to really warm up my body and get loosened up but I did finally feel good, with a light sweat going.
I dove into my squat workout. Lately I've just been doing sets of 20 with lighter weights. My legs have been getting SO sore doing it this way. Just lighter weight and focusing on good form but taking it almost to exhaustion with higher rep ranges.
I felt good. I will say that. I never at any point had that 'LETS DO THIS' feeling that I've had with some of the other stronger pre workouts that I've tried. But I do think that this pre workout helped me during each set to have greater endurance. In that sense it does seem like it's more of a 'fuel' and less of an 'igniter'. I know some pre workouts light you up from the start, but again, this one seems to be much more of a smooth, consistent source of assistance during the workout. I would actually recommend taking the Ascent Pre Workout DURING your workout to extend performance and really give that level of performance through out the whole workout, because I did feel that the effects tapered and faded after about 30 minuets into my workout.
But overall this is a pretty good booster. I personally have been moving away from the big-time 'stim cocktails' such as that garbage MDRN ATHLETE pre from a few months ago. So I really do think this is a pretty good performer even it's not crazy strong. I'll give it an 8/10 on performance. Let's finish up by going over the ingredients.
NOTES: So, this formula is bizarre. Not in a TERRIBLE way, but, eh. Just hear me out. Ingredients lists have to be ordered in the order that they are included by quantity. So the first, and thus primary ingredient ingredient in Ascent Pre Workout is whey protein. Which is odd. There are 4 grams of whey protein and then what? Natural flavors. As the second ingredient. And they tell you leucine is included at 2.5 grams. So that means you have probably around 3.5-4 grams of just flavoring in each serving. Which sounds high to me. It sounds higher than I think it should be. And outside of leucine and caffeine from coffee fruit, you have virtually no other active ingredients. it's strange to me that Ascent would formulate so conservatively... so... bizarrely, really. I mean. There aren't any active ingredients in here! It's no surprise that all you can feel in this pre workout is a tiny bit of energy from the protein + caffeine.
Ascent Native Whey Protein Review: More Sizzle Than Steak?


Working retail—with its new items, bargains, and socializing—can be truly exciting!
But before you sit down at that store employment terminal . . .
Go over to a checkstand, smile, and say, "Would you please page the general manager of the store?"
If the checker says, "What's it about?" say, "I need to ask him how to find something online." This stops any further questions cold, and you'll hear the page.
Then you might hear, "The GM's not in. I can get you the LOD" (leader on duty—it just makes you want to salute). You answer, "Great!" We don't care, since we're all about instant.
During that instant, go over to a vacant register, push the little button on the printer (love that button! Do 55), and tear off a strip about six inches long.
When the manager answers, look serious and say, "I need to speak to you privately for a minute." He'll follow you as you walk away from the checkstand.
Then you execute the Magic Four Hello (Do 1) with the store sentence: "I'm really interested in being a part of your team!"


Building Better Opportunities Herefordshire was launched by Landau in January 2018 to tackle unemployment and economic inactivity in over 19s across Herefordshire, a problem that affects over 6,000 people in the area. The BBO project is funded by the European Social Fund and The National Lottery Community Fund.
Working as a partner in the BBO project, we've created a digital skills shortcourse to help people on the path towards employment.
The course - which launched Spring 2019 - is designed to help build the confidence and skills of those involved, while also giving them an introduction to working in the creative industries.
Our plan is to work with care leavers and young people (19+) to help set them up with workplace skills and tools, and hopefully a creative spark as they step out in to the job market.


text:
People's Embassy of Belarus in Great Britain
People's Embassy
Belarus in British media
Belarus News
Diaspora in the UK
Democratic Belarus
Sviatlana Tsikhanouskaya
National Anti-Crisis Management
Coordination Council
World Belarus Congress
About Belarus
History of belarusian protest
Belarusians Abroad
All people's embassies
The statement on Yara International's announcement
Jan 10, 2022 | Belarus News
Yara, the global crop nutrition company, headquartered in Norway, announced on 10 January 2022 that it would wind-down its relationships with the Belarusian state-owned company Belaruskali from whom Yara is purchasing potassium, representing about 15% of Belaruskali's turnover.
Belarsian diasporas, NGOs and (after some initial hesitation) the office of Sviatlana Tsikhanouskaia have been asking this for months as such Yara's dealings are incompatible with Yara's own ethical rules and provide financal support to Lukashenka's terrorist regime. Despite this huge pressure, a petition with almost 100,000 signatories and 30 protest actions organised in London alone by the Norway's embassy (Norway being Yara's major shareholder) in UK, Yara has been consistently trying to shield behind its alleged concern for "the safety and well-being of Belaruskali workers", while over a hundred of miners were subject to repressions and several are still in jail. Further, recent report provided evidence of Belaruskali's top executives having been continuisly actively engaged in rigging elections in favor of Lukashenka in the city of Salihorsk, where the company is located.
Disappointingly, Yara did not decide to stop supporting the dictator because of their ethical choice, but because the supply chain is paralysed by EU and US sanctions. "The effects on the supply chain from the current sanctions on Belarus have forced Yara to initiate a wind-down of its sourcing of Belarusian potash" reads Yara's statement. In other words, if it were up to them, Yara would have continued their unethical business, ignoring their previous promise to review the relationships by December 2020 ! Unsurprisingly, Yara says that it would continue the current relationships until the 1st April 2021, the very last deadline of the current US sanctions licence exemption for dealings with Belarusian Potash Company.
Luckily, not all businesses are so greedy and unethical as Yara: several dozens of western companies have already cut ties with the Belarusian regime. The most significant recent examples are Pirelli Group (who decided in December 2021 to stop working with the Belarusian Steel Plant) as well Henkel, Mentholatum, L'Oreal, Sandoz, and JYSK who all stopped placing ads on Belarusian state TV.
Belarus in British media (2)
Belarus News (10)
Diaspora in the UK (17)
In case of emergency, please contact us at [email protected]
© 2020, People's Embassies of Belarus


All you need to know about the user plan.
The user plan is the best option for companies that work with a dedicated full-time team. Pricing is as simple as it gets: you pay just $4.99 per active user per month and can add, archive and re-activate users anytime.
At TrackingTime, you only get billed for the people who use the software each month. User fees are calculated based on the current number of active users in your account and are automatically prorated, so that you always end up paying only for the exact amount of time, in which a user has been active, i.e. marked as active by an account administrator rather than archived, during a given billing period.
For example, say you have only one active user in your account and you're billed $4.99 on the 1st of each month. Every month, we'd automatically create an invoice for your monthly subscription and charge your card on file this amount.
Let's say halfway through your plan's billing cycle (for example on the 15th of November) you invite an additional user to join your account. Now you owe $2.49 (4.99 / 2) for the additional user for the time period between Nov 15 and Nov 30 . This prorated user fee is automatically billed on your next invoice.
The same logic applies when you re-activate a user who was previously archived.
Proration also works in the same way when you archive users. For instance, if you started your current billing cycle with 2 users and half way through it you decide to archive one user, you'll receive a credit for $2.49, which will be automatically billed on your next invoice.


Two Washington state cops have purchased plots next to Josh Powell's young sons to prevent family members from burying him next to the boys he killed.
"The bottom line is, Josh Powell will not be near those two boys," Pierce County Sheriff's Sgt. Ed Troyer told Seattle-based radio station KIRO-FM on Wednesday.
Troyer and Sheriff Paul Pastor used their own money and dipped into funds from Crime Stoppers Tacoma-Pierce County to pay for plots two and four at Woodbine Cemetery in Puyallup, Wash. Troyer is executive director of the Crime Stoppers branch.
Previous What do you do if your god is attacked by atheists?


Although Starbuds opened in December of 2016, we have nearly 8 years of experience. Before recreational marijuana become legal in Washington State, we had already been running a successful medical marijuana business. By having a jump start in this complex business, we've been able to build long-term, trusting relationships with the most seasoned experts in the field. We work hard to source not only the best product, but the best prices for our customers.
Whether you are a seasoned or a first time buyer, when you come to Starbuds, you can shop with ease knowing we have secured the best recreational, high-grade marijuana products, from the most reliable vendors and growers in the business.
Our staff have been trained to take care of each customer as though they are family. We want you to not only feel comfortable, but feel confident about your purchase.


text:
"""Tests for Keras text vectorization preprocessing layer."""

import gc
import itertools
import os
import random

from absl.testing import parameterized
import numpy as np

from tensorflow.python import keras
from tensorflow.python import tf2

from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.eager import def_function
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import tensor_shape
from tensorflow.python.keras import keras_parameterized
from tensorflow.python.keras import testing_utils
from tensorflow.python.keras.layers.preprocessing import integer_lookup
from tensorflow.python.keras.layers.preprocessing import preprocessing_test_utils
from tensorflow.python.keras.utils.generic_utils import CustomObjectScope
from tensorflow.python.ops.ragged import ragged_factory_ops
from tensorflow.python.platform import gfile
from tensorflow.python.platform import test

def _get_end_to_end_test_cases():
  test_cases = (
      {
          "testcase_name":
              "test_ints_soft_vocab_cap",
          # Create an array where 1138 is the most frequent term, followed by
          # 1729, then 725, then 42. This ensures that the vocab accumulator
          # is sorting by frequency.
          "vocab_data":
              np.array([[42], [1138], [1138], [1138], [1138], [1729], [1729],
                        [1729], [725], [725]],
                       dtype=np.int64),
          "input_data":
              np.array([[1138], [1729], [725], [42], [42], [725], [1138], [4]],
                       dtype=np.int64),
          "kwargs": {
              "max_tokens": None,
              "dtype": dtypes.int64,
          },
          "expected_output": [[1], [2], [3], [4], [4], [3], [1], [0]],
          "input_dtype":
              dtypes.int64
      },)

  crossed_test_cases = []
  # Cross above test cases with use_dataset in (True, False)
  for use_dataset in (True, False):
    for case in test_cases:
      case = case.copy()
      if use_dataset:
        case["testcase_name"] = case["testcase_name"] + "_with_dataset"
      case["use_dataset"] = use_dataset
      crossed_test_cases.append(case)

  return crossed_test_cases

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class IntegerLookupLayerTest(keras_parameterized.TestCase,
                             preprocessing_test_utils.PreprocessingLayerTest):

  @parameterized.named_parameters(*_get_end_to_end_test_cases())
  def test_layer_end_to_end_with_adapt(self, vocab_data, input_data, kwargs,
                                       use_dataset, expected_output,
                                       input_dtype):
    cls = integer_lookup.IntegerLookup
    expected_output_dtype = dtypes.int64
    input_shape = input_data.shape

    if use_dataset:
      # Keras APIs expect batched datasets.
      # TODO(rachelim): `model.predict` predicts the result on each
      # dataset batch separately, then tries to concatenate the results
      # together. When the results have different shapes on the non-concat
      # axis (which can happen in the output_mode = INT case for
      # IntegerLookup), the concatenation fails. In real use cases, this may
      # not be an issue because users are likely to pipe the preprocessing layer
      # into other keras layers instead of predicting it directly. A workaround
      # for these unit tests is to have the dataset only contain one batch, so
      # no concatenation needs to happen with the result. For consistency with
      # numpy input, we should make `predict` join differently shaped results
      # together sensibly, with 0 padding.
      input_data = dataset_ops.Dataset.from_tensor_slices(input_data).batch(
          input_shape[0])
      vocab_data = dataset_ops.Dataset.from_tensor_slices(vocab_data).batch(
          input_shape[0])

    with CustomObjectScope({"IntegerLookup": cls}):
      output_data = testing_utils.layer_test(
          cls,
          kwargs=kwargs,
          input_shape=input_shape,
          input_data=input_data,
          input_dtype=input_dtype,
          expected_output_dtype=expected_output_dtype,
          validate_training=False,
          adapt_data=vocab_data)
    self.assertAllClose(expected_output, output_data)

  def test_layer_with_list_input(self):
    vocab = [12, 36, 1138, 42]
    data = [[12, 1138, 42], [42, 1000, 36]]  # Note OOV tokens
    layer = integer_lookup.IntegerLookup(vocabulary=vocab)
    output = layer(data)
    expected_output = np.array([[1, 3, 4], [4, 0, 2]])
    self.assertEqual(output.numpy().tolist(), expected_output.tolist())

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class CategoricalEncodingInputTest(
    keras_parameterized.TestCase,
    preprocessing_test_utils.PreprocessingLayerTest):

  def test_sparse_int_input(self):
    vocab_data = np.array([10, 11, 12, 13], dtype=np.int64)
    input_array = sparse_tensor.SparseTensor(
        indices=[[0, 0], [1, 2]],
        values=np.array([13, 32], dtype=np.int64),
        dense_shape=[3, 4])

    expected_indices = [[0, 0], [1, 2]]
    expected_values = [4, 0]
    expected_dense_shape = [3, 4]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64, sparse=True)
    layer = integer_lookup.IntegerLookup(max_tokens=None)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_data = model.predict(input_array, steps=1)
    self.assertAllEqual(expected_indices, output_data.indices)
    self.assertAllEqual(expected_values, output_data.values)
    self.assertAllEqual(expected_dense_shape, output_data.dense_shape)

  def test_ragged_int_input(self):
    vocab_data = np.array([10, 11, 12, 13], dtype=np.int64)
    input_array = ragged_factory_ops.constant([[10, 11, 13], [13, 12, 10, 42]],
                                              dtype=np.int64)
    expected_output = [[1, 2, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64, ragged=True)
    layer = integer_lookup.IntegerLookup(max_tokens=None)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class CategoricalEncodingMultiOOVTest(
    keras_parameterized.TestCase,
    preprocessing_test_utils.PreprocessingLayerTest):

  def test_sparse_int_input_multi_bucket(self):
    vocab_data = np.array([10, 11, 12, 13], dtype=np.int64)
    input_array = sparse_tensor.SparseTensor(
        indices=[[0, 0], [1, 2]],
        values=np.array([13, 133], dtype=np.int64),
        dense_shape=[3, 4])

    expected_indices = [[0, 0], [1, 2]]
    expected_values = [6, 2]
    expected_dense_shape = [3, 4]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64, sparse=True)
    layer = integer_lookup.IntegerLookup(
        max_tokens=None,
        dtype=dtypes.int64,
        num_oov_indices=2,
        mask_token=0,
        oov_token=-1)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_data = model.predict(input_array, steps=1)
    self.assertAllEqual(expected_indices, output_data.indices)
    self.assertAllEqual(expected_values, output_data.values)
    self.assertAllEqual(expected_dense_shape, output_data.dense_shape)

  def test_ragged_int_input_multi_bucket(self):
    vocab_data = np.array([10, 11, 12, 13], dtype=np.int64)
    input_array = ragged_factory_ops.constant([[10, 11, 13], [13, 12, 10, 133]],
                                              dtype=np.int64)
    expected_output = [[2, 3, 5], [5, 4, 2, 1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64, ragged=True)
    layer = integer_lookup.IntegerLookup(max_tokens=None, num_oov_indices=2)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class CategoricalEncodingAdaptTest(
    keras_parameterized.TestCase,
    preprocessing_test_utils.PreprocessingLayerTest):

  def test_sparse_adapt(self):
    vocab_data = sparse_tensor.SparseTensor(
        indices=[[0, 0], [0, 1], [1, 2]],
        values=[203, 1729, 203],
        dense_shape=[3, 4])
    vocab_dataset = dataset_ops.Dataset.from_tensors(vocab_data)

    layer = integer_lookup.IntegerLookup()
    layer.adapt(vocab_dataset)
    expected_vocabulary = [-1, 203, 1729]
    self.assertAllEqual(expected_vocabulary, layer.get_vocabulary())

  def test_ragged_adapt(self):
    vocab_data = ragged_factory_ops.constant([[203], [1729, 203]])
    vocab_dataset = dataset_ops.Dataset.from_tensors(vocab_data)

    layer = integer_lookup.IntegerLookup()
    layer.adapt(vocab_dataset)
    expected_vocabulary = [-1, 203, 1729]
    self.assertAllEqual(expected_vocabulary, layer.get_vocabulary())

  def test_single_int_generator_dataset(self):

    def word_gen():
      for _ in itertools.count(1):
        yield random.randint(0, 100)

    ds = dataset_ops.Dataset.from_generator(word_gen, dtypes.int64,
                                            tensor_shape.TensorShape([]))
    batched_ds = ds.take(2)
    input_t = keras.Input(shape=(), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        max_tokens=10, num_oov_indices=0, mask_token=None, oov_token=None)
    _ = layer(input_t)
    layer.adapt(batched_ds)

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class IntegerLookupOutputTest(keras_parameterized.TestCase,
                              preprocessing_test_utils.PreprocessingLayerTest):

  def test_int_output(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup()
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_output_shape(self):
    input_data = keras.Input(shape=(4,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(max_tokens=2, num_oov_indices=1)
    int_data = layer(input_data)
    self.assertAllEqual(int_data.shape[1:], input_data.shape[1:])

  def test_int_output_with_mask(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[2, 3, 4, 5], [5, 4, 2, 1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(max_tokens=None, mask_token=0)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_explicit_vocab(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data,
        max_tokens=None,
    )
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_explicit_vocab_with_special_tokens(self):
    vocab_data = [0, -1, 42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[2, 3, 4, 5], [5, 4, 2, 1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data,
        max_tokens=None,
        mask_token=0,
    )
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_no_oov(self):
    vocab_data = [42, 1138, 725, 1729]
    valid_input = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 0]])
    invalid_input = np.array([[42, 1138, 725, 203], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, mask_token=0, num_oov_indices=0)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_data = model.predict(valid_input)
    self.assertAllEqual(expected_output, output_data)
    with self.assertRaisesRegex(errors.InvalidArgumentError,
                                "found OOV values.*203"):
      _ = model.predict(invalid_input)

  def test_inverse_output(self):
    vocab_data = [-1, 42, 1138, 725, 1729]
    input_array = np.array([[1, 2, 3, 4], [4, 3, 1, 0]])
    expected_output = np.array([[42, 1138, 725, 1729], [1729, 725, 42, -1]])

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(invert=True)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_forward_backward_explicit_vocab(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = np.array([[42, 1138, 725, 1729], [1729, 725, 42, -1]])

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_data)
    inverse_layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, invert=True)
    int_data = layer(input_data)
    inverse_data = inverse_layer(int_data)
    model = keras.Model(inputs=input_data, outputs=inverse_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_forward_backward_adapted_vocab(self):
    adapt_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = np.array([[42, 1138, 725, 1729], [1729, 725, 42, -1]])

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup()
    layer.adapt(adapt_data)
    inverse_layer = integer_lookup.IntegerLookup(
        vocabulary=layer.get_vocabulary(), invert=True)
    int_data = layer(input_data)
    inverse_data = inverse_layer(int_data)
    model = keras.Model(inputs=input_data, outputs=inverse_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class IntegerLookupVocabularyTest(
    keras_parameterized.TestCase,
    preprocessing_test_utils.PreprocessingLayerTest):

  def _write_to_temp_file(self, file_name, vocab_list):
    vocab_path = os.path.join(self.get_temp_dir(), file_name + ".txt")
    with gfile.GFile(vocab_path, "w") as writer:
      for vocab in vocab_list:
        writer.write(str(vocab) + "\n")
      writer.flush()
      writer.close()
    return vocab_path

  def test_int_output_explicit_vocab(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_no_vocab(self):
    with self.assertRaisesRegex(ValueError,
                                "You must set the layer's vocabulary"):
      layer = integer_lookup.IntegerLookup()
      layer([[1]])

  def test_one_hot_output(self):
    vocab_data = [2, 3, 4, 5]
    input_array = np.array([2, 3, 4, 5, 6])
    expected_output = [
        [0, 1, 0, 0, 0],
        [0, 0, 1, 0, 0],
        [0, 0, 0, 1, 0],
        [0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0],
    ]

    input_data = keras.Input(shape=(1,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, output_mode="one_hot")
    res = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=res)
    output_data = model.predict(input_array)
    self.assertAllEqual(expected_output, output_data)

  def test_multi_hot_output(self):
    vocab_data = [2, 3, 4, 5]
    input_array = np.array([[2, 2, 3, 4], [0, 1, 5, 2]])
    expected_output = [[0, 1, 1, 1, 0], [1, 1, 0, 0, 1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, output_mode="multi_hot")
    res = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=res)
    output_data = model.predict(input_array)
    self.assertAllEqual(expected_output, output_data)

  def test_count_output(self):
    vocab_data = [2, 3, 4, 5]
    input_array = np.array([[2, 2, 3, 4], [0, 1, 5, 6]])
    expected_output = [[0, 2, 1, 1, 0], [3, 0, 0, 0, 1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, output_mode="count")
    res = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=res)
    output_data = model.predict(input_array)
    self.assertAllEqual(expected_output, output_data)

  def test_sparse_output(self):
    vocab_data = [2, 3, 4, 5]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_data, output_mode="multi_hot", sparse=True)
    res = layer(input_data)
    self.assertTrue(res.__class__.__name__, "SparseKerasTensor")

  def test_get_vocab_returns_int(self):
    vocab_data = [42, 1138, 725, 1729]
    expected_vocab = [-1, 42, 1138, 725, 1729]
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_data)
    layer_vocab = layer.get_vocabulary()
    self.assertAllEqual(expected_vocab, layer_vocab)
    self.assertIsInstance(layer_vocab[0], np.int64)

  def test_int_output_explicit_vocab_from_file(self):
    vocab_list = [42, 1138, 725, 1729]
    vocab_path = self._write_to_temp_file("vocab_file", vocab_list)

    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_path)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_inverted_vocab_from_file(self):
    vocab_list = [42, 1138, 725, 1729]
    vocab_path = self._write_to_temp_file("vocab_file", vocab_list)

    input_array = np.array([[1, 2, 3, 4], [4, 3, 1, 0]])
    expected_output = [[42, 1138, 725, 1729], [1729, 725, 42, -1]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_path, invert=True)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_inverted_vocab_from_file_with_mask(self):
    vocab_list = [42, 1138, 725, 1729]
    vocab_path = self._write_to_temp_file("vocab_file", vocab_list)

    input_array = np.array([[2, 3, 4, 5], [5, 4, 2, 0]])
    expected_output = [[42, 1138, 725, 1729], [1729, 725, 42, -10]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(
        vocabulary=vocab_path, invert=True, mask_value=-10)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_int_output_explicit_vocab_from_file_via_setter(self):
    vocab_list = [42, 1138, 725, 1729]
    vocab_path = self._write_to_temp_file("vocab_file", vocab_list)

    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup()
    layer.set_vocabulary(vocab_path)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(expected_output, output_dataset)

  def test_non_unique_vocab_fails(self):
    vocab_data = [42, 1138, 725, 1729, 1729]
    with self.assertRaisesRegex(ValueError, ".*repeated term.*1729.*"):
      _ = integer_lookup.IntegerLookup(vocabulary=vocab_data)

  def test_non_unique_vocab_from_file_fails(self):
    vocab_list = [42, 1138, 725, 1729, 42]
    vocab_path = self._write_to_temp_file("repeat_vocab_file", vocab_list)
    with self.assertRaisesRegex(
        errors_impl.FailedPreconditionError,
        ".*HashTable has different value for same key.*42.*"):
      _ = integer_lookup.IntegerLookup(vocabulary=vocab_path)

  def test_tensor_vocab(self):
    vocab_data = [-1, 42, 1138, 725, 1729]
    vocab_tensor = constant_op.constant(vocab_data, dtypes.int64)
    layer = integer_lookup.IntegerLookup(vocabulary=vocab_tensor)
    returned_vocab = layer.get_vocabulary()
    self.assertAllEqual(vocab_data, returned_vocab)
    self.assertAllEqual(layer.vocabulary_size(), 5)
    fn = def_function.function(lambda: layer.set_vocabulary(vocab_tensor))
    with self.assertRaisesRegex(RuntimeError, "Cannot set a tensor vocabulary"):
      fn()

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class IntegerLookupErrorTest(keras_parameterized.TestCase,
                             preprocessing_test_utils.PreprocessingLayerTest):

  def test_too_long_vocab_fails_in_single_setting(self):
    vocab_data = [42, 1138, 725, 1729]

    layer = integer_lookup.IntegerLookup(max_tokens=4, num_oov_indices=1)
    with self.assertRaisesRegex(ValueError,
                                "vocabulary larger than the maximum vocab.*"):
      layer.set_vocabulary(vocab_data)

  def test_zero_max_tokens_fails(self):
    with self.assertRaisesRegex(ValueError, ".*max_tokens.*"):
      _ = integer_lookup.IntegerLookup(max_tokens=0, num_oov_indices=1)

@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
class IntegerLookupSavingTest(keras_parameterized.TestCase,
                              preprocessing_test_utils.PreprocessingLayerTest):

  def tearDown(self):
    keras.backend.clear_session()
    gc.collect()
    super(IntegerLookupSavingTest, self).tearDown()

  def test_vocabulary_persistence_across_saving(self):
    vocab_data = [42, 1138, 725, 1729]
    input_array = np.array([[42, 1138, 725, 1729], [1729, 725, 42, 203]])
    expected_output = [[1, 2, 3, 4], [4, 3, 1, 0]]

    # Build and validate a golden model.
    input_data = keras.Input(shape=(None,), dtype=dtypes.int64)
    layer = integer_lookup.IntegerLookup(max_tokens=None, num_oov_indices=1)
    layer.set_vocabulary(vocab_data)
    int_data = layer(input_data)
    model = keras.Model(inputs=input_data, outputs=int_data)
    output_dataset = model.predict(input_array)
    self.assertAllEqual(output_dataset, expected_output)

    # Save the model to disk.
    output_path = os.path.join(self.get_temp_dir(), "tf_keras_saved_model")
    model.save(output_path, save_format="tf")

    # Delete the session and graph to ensure that the loaded model is generated
    # from scratch.
    # TODO(b/149526183): Can't clear session when TF2 is disabled.
    if tf2.enabled():
      keras.backend.clear_session()

    loaded_model = keras.models.load_model(
        output_path,
        custom_objects={"IntegerLookup": integer_lookup.IntegerLookup})

    # Ensure that the loaded model is unique (so that the save/load is real)
    self.assertIsNot(model, loaded_model)

    # Validate correctness of the new model.
    new_output_dataset = loaded_model.predict(input_array)
    self.assertAllEqual(new_output_dataset, expected_output)

if __name__ == "__main__":
  test.main()

redpajama_set_name: RedPajamaGithub